138-使用django制作博客的个人中心页面,并进行关注,取消关注等操作
2020-09-13 23:36 lzhshn 阅读(553) 评论(0) 编辑 收藏 举报【1】首先完善ExUser的信息
这里考虑将username信息“复制”到ExUser,避免总是用User去做各种验证:即uid从user的username获取;
其他各种属性包括昵称、粉丝数、微信、QQ、电子邮件、email、注册时间、最近登录时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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被删除了,和其有关的点赞或关联信息不会被删除!这里先记录一下,后续会解决这个问题。
这里只有被关注者,关注者,关注时间。
1 2 3 4 5 6 7 | 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,二次确认的密码,最近登录时间。另外注册时间会自动保存,这里不用显式操作。
1 2 3 4 5 6 7 8 9 10 11 | 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,所以这里的效果是等价的。
1 2 3 4 | < p > 欢迎: < a href="{% url 'account:personal' user.username %}">{{user.username}}</ a > < a href="{% url 'account:logout' %}" class="menu_item">退出</ a > </ p > |
1 2 3 4 | < p > 分类:{{note.note_category}} 作者:< a href="{% url 'account:personal' note.author %}">{{note.author}}</ a > </ p > |
1 2 3 4 5 6 7 8 9 | 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的代码)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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里的两种不一样的写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | {% 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部分,保存,然后重定向。
1 2 3 4 5 6 7 8 9 10 11 | 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | {% 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,而不是当前登录用户!
1 2 3 4 5 6 7 | 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】完成了关注和个人信息页面后,下一步,可以做做简单的社交。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架