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】完成了关注和个人信息页面后,下一步,可以做做简单的社交。