
前言说明
上一篇文章《django树形结构之博客评论案例-基础篇》 介绍了评论的基本实现,包含回复功能
, 但是美中不足的是页面展示无法实现树状层级化展示。
其实说不能实现,也不全对,因为Django默认提供了一个内置过滤器unordered_list
,官网说明参考 。但是 unordered_list
存在两个限制
1、需要单独定义一个递归行数,把数据库中的结果重新保存为嵌套结构,同时需要在view视图中先获取顶层记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def recurse_display(data): """递归展示""" display_list = [] for item in data: display_list.append(item) children = item.children.all() if len(children) > 0: display_list.append(recurse_display(children)) return display_list
def detail(request, id): post = Post.objects.get(pk=id) top_comoments = Comments.objects.filter(parent_comment=None) comoments = recurse_display(top_comments) return render(request, 'comment/detail.html', {'comoments': comoments, "post": post})
|
2、在template中使用 unordered_list
可以进行树状层级展示,但是无法添加样式
MPTT使用
1、安装
2、配置
1 2 3 4 5
| INSTALLED_APPS = [ ... ..., 'mptt', ]
|
3、模型定义
1 2 3 4 5 6 7 8 9 10 11 12
| from mptt.models import MPTTModel, TreeForeignKey class CommentMPTT(MPTTModel): post = models.TreeForeignKey(Post, on_delete=models.DO_NOTHING, verbose_name="评论的文章") ... ... parent_comment = models.ForeignKey('self', null=True, on_delete=models.DO_NOTHING, verbose_name="回复的评论") class MPTTMeta: parent_str = 'parent_comment'
|
3、视图函数
1 2 3 4 5
| def detail(request, id): def detail(request, id): post = Post.objects.get(pk=id) comoments = Comments.objects.all() return render(request, 'comment/detail.html', {'comoments': comoments, "post": post})
|
4、template模板
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
| {% load mptt_tags %}
<div class="row"> <h5>评论列表, 总 <span>{{ comments.count }}</span> 条评论</h5> {% recursetree comments %} {% with comment=node %} <div class="{% if comment.parent_comment %} offset-1 col-11 {% else %} col-12{% endif %}"> <hr> <p> <strong style="color: orange;"> {{ comment.comment_user }} </strong> {% if comment.parent_comment %} <strong style="color: orange;"> => {{ comment.parent_comment.comment_user }} </strong> {% endif %} </p> <div> {{ comment.comment_body|safe }} </div> <div> <span style="color: gray;">{{ comment.comment_time|date:"Y-m-d H:i" }}</span> <button class="reply" username="{{ comment.comment_user }}" pk="{{ comment.pk }}">Reply</button> </div>
{% if not comment.is_leaf_node %} <div class="children"> {{ children }} </div> {% endif %} </div> {% endwith%} {% endrecursetree %} </div>
|
然后进行评论
和回复评论测试
,效果如下

这里发现,针对同一个顶级评论下的多次评论然后产生多级层次,如果回复很多,将会导致无限层级
在页面展示上也不美观
所以一般我们都是按照两级层级展示
,所以这里需要对comment
视图函数做修改
1 2 3 4 5 6 7
|
if pid: parent_comment = CommentMPTT.objects.get(id=pid) new_comment.parent_comment_id = parent_comment.get_root().id
|
然后进行评论
和回复评论测试
,效果就按照如期的两级层级
展示了

至此,大家是不是觉得完美了呢?
其实还是有个小问题的,不知道大家发现没有
李四回复张三的时候,消息显示@张三 xxxx
是没有问题,但是在消息头 谁回复谁
这里是不是都成了 回复 Colin, 也就是全部回复顶级父节点
了
扩展
两种解决办法
1、不展示消息头部的 谁回复谁
,因为在回复消息的时候,已经 @
回复的人了。
同时这里可以从页面设置不同颜色来体现,然后再后台也针对回复的人发送消息
2、如果非要展示,那就在模型中在添加一个字段属性 reply_to ,然后在 comment
视图函数中修改 reply_to的具体人
1 2 3 4 5 6 7 8 9 10
|
if pid: parent_comment = CommentMPTT.objects.get(id=pid) new_comment.parent_comment_id = parent_comment.get_root().id new_comment.reply_to = parent_comment.comment_user
|
然后在template模型中修改
1 2 3 4 5
| {% if comment.parent_comment %} <strong style="color: orange;"> => {{ comment.reply_to }} </strong> {% endif %}
|
最终的效果如下

完美实现~ 项目源码详见
https://gitee.com/colin5063/django_learning_v2/tree/django_blog_comment_v2/
如果觉得文章对你有用,请不吝点赞和关注公众号搜索 全栈运维
或者 DailyJobOps
个人博客 http://blog.colinspace.com/
知乎平台 https://www.zhihu.com/people/colin-31-49
CSDN平台 https://blog.csdn.net/eagle5063
简书平台 https://www.jianshu.com/u/6d793fbacc88