代码改变世界

138-使用django制作博客的个人中心页面,并进行关注,取消关注等操作

2020-09-13 23:36  lzhshn  阅读(524)  评论(0编辑  收藏  举报

【1】首先完善ExUser的信息

这里考虑将username信息“复制”到ExUser,避免总是用User去做各种验证:即uid从user的username获取;

其他各种属性包括昵称、粉丝数、微信、QQ、电子邮件、email、注册时间、最近登录时间。

class ExUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='ex_user')
    uid = models.CharField(max_length=128, default='username')
    nickname = models.CharField(max_length=128, default='默认昵称')
    fans_count = models.BigIntegerField(default=0)
    confirm_password = models.CharField(max_length=32)
    wechat_id = models.CharField(max_length=128, unique=True, blank=True, null=True)
    qq_num = models.BigIntegerField(unique=True, blank=True, null=True)
    email = models.EmailField(unique=True, blank=True, null=True)
    tel_num = models.BigIntegerField(unique=True, blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)
    last_login_time = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return self.uid

  

【2】新建一个用于反映关注关系的模型。

一些不是很重要的信息,没必要做关联性,之前解决点赞时是这个思路,这里同样。不过这种方式并不推荐在生产环境下被使用,因为如果对应的user或note被删除了,和其有关的点赞或关联信息不会被删除!这里先记录一下,后续会解决这个问题。

这里只有被关注者,关注者,关注时间。

class FollowRelation(models.Model):
    followed = models.CharField(max_length=128, blank=True, null=True)
    following = models.CharField(max_length=128, blank=True, null=True)
    follow_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.followed, '<<<', self.following

  

【3】显示个人信息。

这里的逻辑比较复杂,在用一个views函数渲染个人页面之前,还有关键两步要做:

 

1-让ExUser的uid记住所对应的那个User的username,这个操作在注册时完成。

可以发现,在完成注册时,user所对应的那个exuser需要保存几个关键信息:uid,二次确认的密码,最近登录时间。另外注册时间会自动保存,这里不用显式操作。

                    new_user = User.objects.create_user(username=username, password=password)
                    # 注意!由于一对一的关系,当一个User被注册(also created)时,一个ExUser对象被创建,因此这里需要用新建实例的方式来创建对象,然后存储对象
                    # 但是已经存在的ExUser,不能用这种方式
                    new_exuser = ExUser(user=new_user)
                    new_exuser.uid = new_user.username
                    new_exuser.confirm_password = confirm_password
                    new_exuser.last_login_time = datetime.datetime.now()
                    new_exuser.save()
                    # 注册完成后,同时进行登录
                    login(request, new_user)
                    return HttpResponseRedirect(reverse('notebook:start_page'))

 

2-将所有指向个人信息的链接中,统一传递username。

第一个是查看自己的个人信息,传递的是user.username;

第二个是查看别人的个人信息(在网站里有很多个这样的链接,这里以其中一个为例),由于文章的作者,其实就是某个user的username,即:new_note.author = current_user.username,所以这里的效果是等价的。

            <p> 欢迎:
                <a href="{% url 'account:personal' user.username %}">{{user.username}}</a>
                <a href="{% url 'account:logout' %}" class="menu_item">退出</a>
            </p>

  

    <p>
        分类:{{note.note_category}}
        作者:<a href="{% url 'account:personal' note.author %}">{{note.author}}</a>
    </p>

 

        form = NoteForm(request.POST)
        if form.is_valid():
            new_note = form.save(commit=False)
            new_note.user = current_user
            new_note.author = current_user.username
            new_note.save()
            # Without this next line the tags won't be saved.
            form.save_m2m()
            return HttpResponseRedirect(reverse('notebook:one_note', args=[new_note.pk]))

  

  

3-现在看具体的views函数。解析如下:

 

在进入if——else之前,先获取3个内容,之后都要作为上下文传入。

the_exuser:根据uid获取,这个页面因为只查看,不能编辑,所以全部用the_exuser点出来,没有使用modelform;

if_follow:判断是否已经有对应的记录,如果有表示已经关注了,可以取消;如果没有,提供关注操作;

最后特别要注意username,这个属性在这里要多次传入传出。

 

if部分,根据上下文,呈现信息页面;

else部分,首先新建一个关注模型,然后写入被关注者,关注者,再存储这个模型;之后,将对应的exuser的粉丝数+1,然后保存exuser;

完成所有操作后,重定向到个人信息页面(相当于执行if的代码)。

 

def personal(request, username):
    # 这里统一使用用户名(username --> uid)的方式来寻找最终用户,然后对应到他的(1对1)的扩展模型里
    # 在实际生产环境里,如果显示的是昵称,方法类似,nickname=nickname
    # 由于exuser并不是所有的字段都适合用form来呈现,因此需要同时传入两个上下文
    the_exuser = ExUser.objects.get(uid=username)
    form = ExUserForm(instance=the_exuser)
    if_follow = FollowRelation.objects.filter(followed=username, following=request.user.username).exists()

    if request.method != 'POST':
        context = {'the_exuser': the_exuser, 'username': username, 'if_follow': if_follow}
        return render(request, 'personal_info.html', context)

    else:
        new_follow = FollowRelation()
        new_follow.followed = username
        new_follow.following = request.user.username
        new_follow.save()
        the_exuser = ExUser.objects.get(uid=username)
        the_exuser.fans_count += 1
        the_exuser.save()

        return HttpResponseRedirect(reverse('account:personal', args=[username]))

  

【4】看具体的个人信息的模板。

个人信息分为两部分:基础信息,个人资料。都用the_exuser点出来;

在之后,先判断进来的这个人,看的是不是自己的个人信息页面,如果是,让他可以编辑;

如果不是,再判断有没有关注过,如果有,则可以进行取消,如果没有,则可以进行关注。

最后这里还有一个要点:

如果一个函数在渲染模板后,还需要从模板那里接收返回操作(post),即便是很简单的内容,也可以放在form里,只是没有具体的值传递,但是可以检测到post,进行其他一些操作;

如果一个函数仅仅只做一次操作,则通过一个链接指向它即可。读者可以看一下if--else里的两种不一样的写法。 

 

{% block content %}
<div class="left">

    {# show user info and edit #}
    <div id="basic">
        <h4>基础信息</h4>
        <table>
            <tr>
                <td class="label">用户名</td>
                <td class="value">{{the_exuser.uid}}</td>
            </tr>

            <tr>
                <td class="label">注册时间</td>
                <td class="value">{{the_exuser.create_time}}</td>
                <td class="label">最近登录</td>
                <td class="value">{{the_exuser.last_login_time}}</td>
            </tr>

            <tr>
                <td>粉丝数</td>
                <td>{{the_exuser.fans_count}}</td>
            </tr>
        </table>
    </div>

    <div id="personal">
        <h4>个人资料</h4>

        <table>
                <tr>
                    <td class="label">昵称</td>
                    <td class="value">{{the_exuser.nickname}}</td>
                </tr>

                <tr>
                    <td class="label">微信号</td>
                    <td class="value">{{the_exuser.wechat_id}}</td>
                </tr>

                <tr>
                    <td class="label">QQ号</td>
                    <td class="value">{{the_exuser.qq_num}}</td>
                </tr>

                <tr>
                    <td class="label">电子邮件</td>
                    <td class="value">{{the_exuser.email}}</td>
                </tr>

                <tr>
                    <td class="label">电话</td>
                    <td class="value">{{the_exuser.tel_num}}</td>
                </tr>
            </table>

        {% if request.user.username == username %}
           <a href="{% url 'account:personal_edit' request.user.username %}"><button>编辑</button></a>
        {% else %}
            {% if if_follow %}
                <a href="{% url 'account:cancel_follow' username %}"><button>取消关注</button></a>
            {% else %}
                <form action="{% url 'account:personal' username %}" method="post">
                    {% csrf_token %}
                    <input type="submit" value="关注ta!" />
                </form>
            {% endif %}
        {% endif %}
    </div>

</div>
{% endblock content %}

  

【5】编辑个人信息。先看views部分。

首先,username,是之前那个views函数(def personal)传入模板的,然后再从那个模板把这个值再传到这个函数里,后续还会从这里传入到编辑模板;

接着是两个要传入上下文的参数,其中的form,通过username --> uid -->exuser --> exuserform获得。

if部分,显示待编辑的页面。不能编辑的部分通过the_exuser点出来,其他部分,通过modelform显示。

else部分,保存,然后重定向。

def personal_edit(request, username):
    the_exuser = ExUser.objects.get(uid=username)
    form = ExUserForm(instance=the_exuser)
    context = {'the_exuser': the_exuser, 'form': form, 'username': username}
    if request.method != 'POST':
        return render(request, 'personal_edit.html', context)
    else:
        form = ExUserForm(instance=the_exuser, data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('account:personal', args=[username]))

  

【6】编辑个人信息,模板部分。

基础信息部分不能被编辑,用点的方式;

剩余部分,用modelform方式。注意:这里结合使用了form和table。

{% block content %}
<div class="left">

    {# show user info and edit #}
    <div id="basic">
        <h4>基础信息</h4>
        <table>
            <tr>
                <td class="label">用户名</td>
                <td class="value">{{the_exuser.uid}}</td>
            </tr>

            <tr>
                <td class="label">注册时间</td>
                <td class="value">{{the_exuser.create_time}}</td>
                <td class="label">最近登录</td>
                <td class="value">{{the_exuser.last_login_time}}</td>
            </tr>

            <tr>
                <td>粉丝数</td>
                <td>{{the_exuser.fans_count}}</td>
            </tr>
        </table>
    </div>

    <div id="personal">
        <h4>个人资料</h4>

            <form action="{% url 'account:personal_edit' request.user.username %}" method="post">
                {% csrf_token %}
                <table>
                <tr>
                    <td class="label">昵称</td>
                    <td class="value">{{form.nickname}}</td>
                </tr>

                <tr>
                    <td class="label">微信号</td>
                    <td class="value">{{form.wechat_id}}</td>
                </tr>

                <tr>
                    <td class="label">QQ号</td>
                    <td class="value">{{form.qq_num}}</td>
                </tr>

                <tr>
                    <td class="label">电子邮件</td>
                    <td class="value">{{form.email}}</td>
                </tr>

                <tr>
                    <td class="label">电话</td>
                    <td class="value">{{form.tel_num}}</td>
                </tr>

                </table>

                <input type="submit" value="提交修改" />

            </form>

    </div>

</div>
{% endblock content %}

  

【7】取消关注。这里只有一个函数进行操作,然后重定向。

取消关注的操作是从个人信息页面来的,那边的链接会传来同一个username。通过传来的username和当前的request.user.username,找到对应的关联模型,然后将它删除即可;

之后再把粉丝数-1,然后把对应的exuser保存;

最后重定向,重定向仍然要传递同一个username参数。

强调:这里多次反复传入传出的参数username,是这个要被操作或浏览的个人信息对应的username,而不是当前登录用户!

def cancel_follow(request, username):
    the_follow = FollowRelation.objects.filter(followed=username, following=request.user.username)
    the_follow.delete()
    the_exuser = ExUser.objects.get(uid=username)
    the_exuser.fans_count -= 1
    the_exuser.save()
    return HttpResponseRedirect(reverse('account:personal', args=[username]))

  

【8】完成了关注和个人信息页面后,下一步,可以做做简单的社交。