django(19)多级评论树结构
Posted fqh202
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django(19)多级评论树结构相关的知识,希望对你有一定的参考价值。
评论列表转换
列表转换前
comment_list = [
{'id': 1, 'content': '1111', 'parent_id': None, 'children_contents':[]},
{'id': 2, 'content': '222', 'parent_id': 1, 'children_contents':[]},
{'id': 3, 'content': '33333', 'parent_id': 1, 'children_contents':[]},
{'id': 4, 'content': '4444', 'parent_id': 3, 'children_contents':[]},
{'id': 5, 'content': '555555', 'parent_id': None, 'children_contents':[]},
{'id': 6, 'content': '666', 'parent_id': 5, 'children_contents':[]},
{'id': 7, 'content': '777777', 'parent_id': None, 'children_contents':[]},
{'id': 8, 'content': '777777', 'parent_id': 7, 'children_contents':[]},
]
转换后
comment_list=[
{
'id': 1,
'content': '1111',
'parent_id': None,
'children_contents': [
{'id': 2,
'content': '222',
'parent_id': 1,
'children_contents': []},
{'id': 3,
'content': '33333',
'parent_id': 1,
'children_contents': [
{'id': 4,
'content': '4444',
'parent_id': 3,
'children_contents': []
}]
}]
}, {
'id': 5,
'content': '555555',
'parent_id': None,
'children_contents': [
{'id': 6,
'content': '666',
'parent_id': 5,
'children_contents': []
}]
}, {
'id': 7,
'content': '777777',
'parent_id': None,
'children_contents': [
{'id': 8,
'content': '777777',
'parent_id': 7,
'children_contents': []
}]
}]
功能函数
def transform_list(comment_list):
comment_dict = {}
for d in comment_list:
id = d.get('id')
comment_dict[id] = d
for k in comment_dict:
parent_id = comment_dict[k]['parent_id']
if parent_id:
comment_dict[parent_id]['children_contents'].append(comment_dict[k])
res_list = []
for i in comment_dict:
if not comment_dict[i]['parent_id']:
res_list.append(comment_dict[i])
return res_list
new_lst = transform_list(comment_list)
print(new_lst)
推倒步骤
1、模拟取出某文章的所有评论的部分信息如下:
comment_list = [
{'id': 1, 'content': '1111', 'parent_id': None, 'children_contents': []},
{'id': 2, 'content': '2222', 'parent_id': None, 'children_contents': []},
{'id': 3, 'content': '3333', 'parent_id': 1, 'children_contents': []},
{'id': 4, 'content': '4444', 'parent_id': 2, 'children_contents': []},
{'id': 5, 'content': '5555', 'parent_id': 4, 'children_contents': []},
{'id': 6, 'content': '6666', 'parent_id': 3, 'children_contents': []},
{'id': 7, 'content': '7777', 'parent_id': 6, 'children_contents': []},
{'id': 8, 'content': '8888', 'parent_id': None, 'children_contents': []},
]
2、进一步构建数据结构, {1: {‘id‘:1, ...}, 2: {‘id‘:2, ...},}
comment_dict = {}
for d in comment_list:
id = d.get('id')
comment_dict[id] = d
'''
{1: {'id': 1, 'content': '...', 'parent_id': None, 'children_contents': []},
2: {'id': 2, 'content': '...', 'parent_id': None, 'children_contents': []},
3: {'id': 3, 'content': '...', 'parent_id': 1, 'children_contents': []},
4: {'id': 4, 'content': '...', 'parent_id': 1, 'children_contents': []},
5: {'id': 5, 'content': '...', 'parent_id': 4, 'children_contents': []},
6: {'id': 6, 'content': '...', 'parent_id': 3, 'children_contents': []},
7: {'id': 7, 'content': '...', 'parent_id': 6, 'children_contents': []},
8: {'id': 8, 'content': '...', 'parent_id': None, 'children_contents': []}}
3、若存在父评论将每个评论放进其parent_id
对应的children_contents
列表中:
for k in comment_dict:
parent_id = comment_dict[k]['parent_id']
if parent_id:
comment_dict[parent_id]['children_contents'].append(comment_dict[k])
'''
{1: {'id': 1, 'content': '...', 'parent_id': None, 'children_contents': [
{'id': 3, 'content': '...', 'parent_id': 1, 'children_contents': [],
{'id': 4, 'content': '...', 'parent_id': 1, 'children_contents': []}
]},
2: {'id': 2, 'content': '...', 'parent_id': None, 'children_contents': []},
3: {'id': 3, 'content': '...', 'parent_id': 1, 'children_contents': [
{'id': 6, 'content': '...', 'parent_id': 3, 'children_contents': []},
]},
4: {'id': 4, 'content': '...', 'parent_id': 1, 'children_contents': [
{'id': 5, 'content': '...', 'parent_id': 4, 'children_contents': []},
]},
5: {'id': 5, 'content': '...', 'parent_id': 4, 'children_contents': []},
6: {'id': 6, 'content': '...', 'parent_id': 3, 'children_contents': [
{'id': 7, 'content': '...', 'parent_id': 6, 'children_contents': []},
]},
7: {'id': 7, 'content': '...', 'parent_id': 6, 'children_contents': []},
8: {'id': 8, 'content': '...', 'parent_id': None, 'children_contents': []},
}
4、筛选出所有的根评论, 整理成列表形式
res_list = []
for i in comment_dict:
if not comment_dict[i]['parent_id']:
res_list.append(comment_dict[i])
遍历根评论(最关键)
需求展示
根评论1
子评论1
子评论1
根评论2
子评论3
子评论4
跟评论3
子评论5
子评论6
实现函数
def get_content(list):
for d in list:
print(d['content'])
if d['children_contents']:
# 递归,调用自身
get_content(d['children_contents'])
# 测试列表
res_list = [
{'id': 1, 'content': '1111', 'parent_id': None, 'children_contents': [
{'id': 2, 'content': '222', 'parent_id': 1, 'children_contents': [
{'id': 8, 'content': '8888888', 'parent_id': 2, 'children_contents': []}]},
{'id': 3, 'content': '33333', 'parent_id': 1, 'children_contents': [
{'id': 4, 'content': '4444', 'parent_id': 3, 'children_contents': []}]}]},
{'id': 5, 'content': '555555', 'parent_id': None, 'children_contents': [
{'id': 6, 'content': '666', 'parent_id': 5, 'children_contents': []}]}, {
'id': 7, 'content': '777777', 'parent_id': None, 'children_contents': []}]
# 调用函数
get_content(res_list)
'''
# 结构展示
1111
222
8888888
33333
4444
555555
666
777777
'''
方法1:自定义标签渲染评论
urls.py
urlpatterns = [
# 方式3、运用自定义标签在前端渲染评论
url(r'^p/(d{1,5})/', views.article_detail_page, name='article'),
]
views.py
def article_detail_page(request, pk):
article = Article.objects.filter(nid=int(pk)).first()
if not article:
return HttpResponse('<h1>资源页面不存在</h1>')
return render(request, 'article_detail_2.html', {
'pk': pk,
'article': article,
})
article_detail_2.html
# 评论处
<div class="blog_comment">
<div class="comment_title">评论列表</div>
<hr>
{# 引用自定义标签渲染html字符串 #}
{% user_comment pk %}
</div>
自定义标签
from django import template
from my_blog.models import *
register = template.Library()
from my_blog.models import Article
from django.shortcuts import HttpResponse
from ..utils import transform_list
def process_menu_data(pk):
"""取出数据列表"""
article = Article.objects.filter(nid=int(pk)).first()
if not article:
return HttpResponse("<h1>资源不存在!</h1>")
comment_list = list(article.comment_set.all().values('nid', 'content', 'user__username', 'parent_id_id', 'create_time'))
comment_list = transform_list(comment_list)
print(comment_list)
return comment_list
'''
[{'nid': 14, 'content': '写得不错', 'user__username': 'alex', 'parent_id_id': None, 'create_time': '2018-08-10 19:47:52', 'children_contents': [
{'nid': 15, 'content': '我也这个认为', 'user__username': 'alex', 'parent_id_id': 14, 'create_time': '2018-08-10 19:47:58', 'children_contents': [
{'nid': 16, 'content': '认同', 'user__username': 'bob', 'parent_id_id': 15, 'create_time': '2018-08-10 19:48:38', 'children_contents': []}]}]},
{'nid': 17, 'content': '有个问题', 'user__username': 'bob', 'parent_id_id': None, 'create_time': '2018-08-10 19:48:54', 'children_contents': [
{'nid': 18, 'content': '啥问题', 'user__username': 'alex', 'parent_id_id': 17, 'create_time': '2018-08-10 19:49:06', 'children_contents': [
{'nid': 19, 'content': '不告诉你', 'user__username': 'bob', 'parent_id_id': 18, 'create_time': '2018-08-10 19:49:16', 'children_contents': [
{'nid': 20, 'content': '为什么不告诉我', 'user__username': 'alex', 'parent_id_id': 19, 'create_time': '2018-08-10 19:49:25', 'children_contents': [
{'nid': 21, 'content': '不喜欢你', 'user__username': 'bob', 'parent_id_id': 20, 'create_time': '2018-08-10 19:49:31', 'children_contents': []}]}]}]}]}]
'''
def produce_html(comment_list):
"""拼接评论列表html字符串"""
html = ''
tpl1 = """
<div class="comment_item">
<div class="comment-header">
<a href="#"><span class="user_name">{0}</span></a>
<span class="comment_time">{1}</span>
<span class="comment_content">{2}</span>
<span id="{3}" hidden></span>
<a class="reply">回复</a>
</div>
<div class="comment-body">{4}</div>
</div>
"""
for item in comment_list:
# 因为children_contents中内容结构是相同的
if item['children_contents']:
html += tpl1.format(item['user__username'], str(item['create_time'])[:19], item['content'].strip(), item['nid'],
produce_html(item["children_contents"]))
else:
html += tpl1.format(item['user__username'], str(item['create_time'])[:19], item['content'].strip(), item['nid'], '')
return html
from django.utils import safestring
# 引入菜单标签到模板中
@register.simple_tag
def user_comment(pk):
# 1. 判断用户是否登录,即查看有没有权限列表
# 2. 若为登录用户,则调动自定义函数从数据库取到相关的评论数据
data = process_menu_data(pk)
# 3. 生成html, 注意转换成可以渲染的html
html = safestring.mark_safe(produce_html(data))
return html
评论回复 & 普通评论 思路
ajax代码
<script>
// 扩展js的功能,可以使用字符串拼接功能
String.prototype.format= function(){
var args = arguments;
return this.replace(/{(d+)}/g,function(s,i){
return args[i];
});
};
var parent_comment_id = ""; // 设置全局变量,当点击回复按钮则赋值为点击的评论的id;
var reply_ele = '';
// 回复评论事件
$('.blog_comment').on('click', '.reply', function () {
// 1、取出该条评论的id值,传到后端
parent_comment_id = $(this).prev().attr('id');
// 2、定位到当前items的body区域,以添加子评论
reply_ele = $(this).parent().next();
// 3、聚焦到评论框
$('#comment_input').focus()
});
// 增加文章评论
$("#comment_submit_btn").click(function () {
// 4、取出输入的评论内容
var comment_content=$("#comment_input").val().trim();
// 5、查看当前用户是否登录,因为没有登录就不能取到user
{% if request.user.is_authenticated %}
$.ajax({
url:"/blog/comment/",
type:"post",
data:{"csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val(),
"article_id":{{ article.nid }},
"comment_content":comment_content,
"parent_comment_id":parent_comment_id
},
success:function (data) {
// 评论内容模板
s = ' <div class="comment_item"> <div class="comment-header"> <a href="#"><span class="user_name">{0}</span></a> <span class="comment_time">{1}</span> <span class="comment_content">{2}</span> <span id="{3}" hidden></span> <a class="reply">回复</a> </div> <div class="comment-body">{4}</div> </div>';
// 10、拼接html字符串
s=s.format('{{ request.user.username }}',
data.comment_createTime,
comment_content,
data.comment_id, '');
// 11、动态添加新评论
if (reply_ele){
// 若存在,则说明是子评论,且已定位到子评论的body区域
reply_ele.append(s);
}
else
{
// 不通过回复按钮添加
$('.blog_comment').append(s)
}
// 12、清空评论区域
$("#comment_input").val("");
// 13、关键点, 每次走完此处必须对全局变量清零
father_comment_username="";
parent_comment_id=0;
reply_ele = '';
}
});
{% else %}
// 6、没有登录则直接发送登录请求并携带当期页面的path
location.href="/login?path={{ request.path }}";
{% endif %}
});
</script>
关于登录后跳转回原页面
1、原页面:
<script>
{% if request.user.is_authenticated %}
...
{% else %}
// 6、没有登录则直接发送登录请求并携带当期页面的path
location.href="/login?path={{ request.path }}";
{% endif %}
</script>
2、始终保证在流程中保存path参数即可最终若是ajax请求
- 若最终跳转是ajax请求,则使用:
window.location.href= data.path;
- 若是view逻辑,则使用
redirect(path)
def my_login(request):
if request.method == "POST":
path = request.POST.get('path', '')
pass
elif request.method == "GET":
# 获取path路径
path = request.GET.get('path', '')
if not path:
path = '/index/'
print('path', path)
return render(request, 'login.html', {
'path': path
})
views.py
def user_comment(request):
"""方法1, 直接在前端操作评论内容"""
# 6、取出传入的评论的相关信息
user_id = request.user.nid
article_id = request.POST.get("article_id")
comment_content = request.POST.get("comment_content").strip()
if not comment_content:
return HttpResponse('noy ok')
# 7、创建新评论
if request.POST.get("parent_comment_id"):
# 7、 获取该评论的父级评论id并保存记录
c = int(request.POST.get("parent_comment_id"))
comment_obj = Comment.objects.create(article_id=article_id, content=comment_content, user_id=user_id,
parent_id_id=c)
else:
comment_obj = Comment.objects.create(article_id=article_id, content=comment_content, user_id=user_id)
from django.db.models import F
# 8、对评论数量自加操作
Article.objects.filter(nid=article_id).update(comment_num=F("comment_num") + 1)
# 9、只需要传输创建时间过去
response_ajax = {"comment_createTime": str(comment_obj.create_time)[:16], 'comment_id':comment_obj.nid} # 很关键,不取毫秒!
return HttpResponse(json.dumps(response_ajax), content_type='application/json')
具体参考,见实战项目
- 地址:https://github.com/Fangqihan/blog_sys
- mysql数据库还原:用户名root,密码abc123
以上是关于django(19)多级评论树结构的主要内容,如果未能解决你的问题,请参考以下文章