人生苦短
我用Python

Django022-打造通用多级评论模型

Django022-打造通用多级评论模型

多级评论在很多地方都是可以使用的,便于以后的使用,避免重复造轮子,自己写一个通用的模型。

Django自带的多级评论,请参考这里:Django默认多级评论

下面我们要实现的是通用多级评论,当我们脱离使用Django后,一样能够写出多级评论,如:使用轻量级框架FlaskBottle;异步框架Tonardo

在写代码之前,我们需要先分析常见的评论结构,找到一定的规律。市面上有很多免费的评论插件,使用起来也是很方便的,但毕竟是第三方的东西,虽然免费了,不过数据是保存在人家那里,自己没有办法拿到数据,同时第三方的插件也拖慢网站的访问速度,影响用户体验。

第三方评论插件:多说,displus等

简单的评论模型:

这种评论结构很常见,也很通用!

通过模型我们需要简单的构造一些数据,这里我们通过DjangoModel来构建数据库,比较简单快捷!

我们需要这些内容:

  • 文章ID,文章标题
  • 用户ID,用户信息
  • 评论ID,评论内容,评论时间,被评论的ID(也就是被评论的父级ID)

构建简单的评论模型这些数据基本上够用了,若想添加更多信息,只需要添加额外的字段即可。

1. 创建数据库表结构及数据

应用的models.py文件:

from django.db import models

class UserInfo(models.Model):
username = models.CharField(max_length=32, unique=True) #只需要写这个就行,下面的是我之前写的,加不加都可以
password = models.CharField(max_length=32)
email = models.EmailField(max_length=64, unique=True)
current_time = models.DateTimeField()

def __str__(self):
return self.username

# 多级评论测试
class New(models.Model):
title = models.CharField(max_length=32)

class Comment(models.Model):
content = models.CharField(max_length=128)
ctime = models.DateTimeField(auto_now_add=True, null=True)
new = models.ForeignKey('New')
user_info = models.ForeignKey('UserInfo')
parent = models.ForeignKey('self', related_name='redirect', null=True) #外键到自己,获取父级的评论ID

生成的数据库表,并添加数据如下:

用户信息表:

文章表:

评论表:

表结构已经创建,接下来就要写逻辑代码,这里还是通过Django来实现,并不局限于Django哦!只是现在用起来方便而已。。。懒得去写

先写好URL和简单的模板comment.html,然后在视图views.py中写逻辑代码。

将评论表中的数据取出来,做一些简单的分析,缕清楚数据结构:

直接取出的数据内容:

#
obj = models.Comment.objects.values()
print(obj)
#数据库内容如下:
obj_list= [
{'new_id': 1, 'content': 'PHP是世界上最好的语言,因为ta是拍黄片的!', 'parent_id': None, 'id': 1, 'user_info_id': 2, 'ctime': None},
{'new_id': 2, 'content': '法海他不懂爱,不然早就把白娘子XXOO,哪有许仙的机会。。。', 'parent_id': None, 'id': 2, 'user_info_id': 3, 'ctime': None},
{'new_id': 3, 'content': '这个嘛,没法确定。。。', 'parent_id': None, 'id': 3, 'user_info_id': 4, 'ctime': None},
{'new_id': 1, 'content': '这是要开战a,C才是最好的语言,PHP滚逼。。。', 'parent_id': 1, 'id': 4, 'user_info_id': 1, 'ctime': None},
{'new_id': 2, 'content': '法海估计是不能。。。^_^', 'parent_id': 2, 'id': 5, 'user_info_id': 3, 'ctime': None}
]

看着有点乱,整理一下便于分析:

comment_list = [
{'id': 1, 'new_id': 1, 'content': 'PHP是世界上最好的语言,因为ta是拍黄片的!', 'parent_id': None, 'user_info_id': 2, 'ctime': None},
{'id': 2, 'new_id': 2, 'content': '法海他不懂爱,不然早就把白娘子XXOO,哪有许仙的机会。。。', 'parent_id': None, 'user_info_id': 3, 'ctime': None},
{'id': 3, 'new_id': 3, 'content': '这个嘛,没法确定。。。', 'parent_id': None, 'user_info_id': 4, 'ctime': None},
{'id': 4, 'new_id': 1, 'content': '这是要开战a,C才是最好的语言,PHP滚逼。。。', 'parent_id': 1, 'user_info_id': 1, 'ctime': None},
{'id': 5, 'new_id': 2, 'content': '法海估计是不能。。。^_^', 'parent_id': 2, 'user_info_id': 3, 'ctime': None}
]

我们可以看到parent_id为空,是一级评论给,不为空时,存在下级评论(这里是三级评论),但是我们并不知道下面有几级评论,因为它是动态生成的。逻辑我们是很清楚的,但是要让代码能够更好的体现出来,需要更改数据结构。我们有两种方式来实现逻辑:

首先改变数据结构,添加一个children:[]专门作为存放下级评论的所有内容:

comment_list = [
{'id': 1, 'new_id': 1, 'content': 'PHP是世界上最好的语言,因为ta是拍黄片的!', 'parent_id': None, 'user_info_id': 2, 'ctime': None, 'children':[]},
{'id': 2, 'new_id': 2, 'content': '法海他不懂爱,不然早就把白娘子XXOO,哪有许仙的机会。。。', 'parent_id': None, 'user_info_id': 3, 'ctime': None, 'children':[]},
...
]

基本的数据结构已经实现了,接下来就是逻辑的实现:

2. 数据逻辑实现

  • 递归函数实现多级评论

数据结构和逻辑已经清晰,接下就开始实现逻辑:

#视图函数
def comment(request):
#获取数据
obj = models.Comment.objects.values()
#处理后的数据列表
comment_tree = []
#遍历初始数据
for item in obj:
#判断是否为一级评论
if not item['parent_id']:
#添加存放子评论的空列表
item['children'] = []
#将一级评论添加到处理后数据列表
comment_tree.append(item)
else:
#遍历一级评论
for data in comment_tree:
#判断初始数据是否存在和一级评论有相同的id,存在即是字评论,然后添加
if item['parent_id'] == data['id']:
#添加存放子评论列表
item['children'] = []
#将子评论添加到列表中
data['children'].append(item)
else:
#以下是无限重复,因此,我们使用递归函数来处理

return HttpResponse(json.dumps(comment_tree))

递归实现上述反复出现的逻辑:

#为了方便调用,我们使用类进行封装
class NodeList:
#递归处理
@staticmethod
def recursion(comment_tree, item):
for data in comment_tree:
if item['parent_id'] == data['id']:
item['children'] = []
data['children'].append(item)
return
else:
NodeList.recursion(data['children'], item)

@staticmethod
def comment(obj):
comment_tree = []
for item in obj:
if not item['parent_id']:
item['children'] = []
comment_tree.append(item)
else:
NodeList.recursion(comment_tree, item)
return comment_tree

基本的逻辑已经用代码实现了,下面贴一下完整的代码,并在前端显示拿到的数据,判断一下是否正确:

def comment(request):
obj = models.Comment.objects.values()
result = NodeList.comment(obj)
return HttpResponse(json.dumps(result))

class NodeList:

@staticmethod
def recursion(comment_tree, item):
for data in comment_tree:
if item['parent_id'] == data['id']:
item['children'] = []
data['children'].append(item)
return
else:
NodeList.recursion(data['children'], item)


@staticmethod
def comment(obj):
comment_tree = []
for item in obj:
if not item['parent_id']:
item['children'] = []
comment_tree.append(item)
else:
NodeList.recursion(comment_tree, item)
return comment_tree

前端显示:

通过在线JSON工具转化后的结果:

[
{
"new_id": 1,
"ctime": null,
"content": "PHP是世界上最好的语言,因为ta是拍黄片的!",
"children": [
{
"new_id": 1,
"ctime": null,
"content": "这是要开战a,C才是最好的语言,PHP滚逼。。。",
"children": [ ],
"user_info_id": 1,
"parent_id": 1,
"id": 4
}
],
"user_info_id": 2,
"parent_id": null,
"id": 1
},
{
"new_id": 2,
"ctime": null,
"content": "法海他不懂爱,不然早就把白娘子XXOO,哪有许仙的机会。。。",
"children": [
{
"new_id": 2,
"ctime": null,
"content": "法海估计是不能。。。^_^",
"children": [
{
"new_id": 2,
"ctime": null,
"content": "你咋知道呢?也许法海。。。。",
"children": [ ],
"user_info_id": 5,
"parent_id": 5,
"id": 6
}
],
"user_info_id": 3,
"parent_id": 2,
"id": 5
}
],
"user_info_id": 3,
"parent_id": null,
"id": 2
},
{
"new_id": 3,
"ctime": null,
"content": "这个嘛,没法确定。。。",
"children": [ ],
"user_info_id": 4,
"parent_id": null,
"id": 3
}
]

上面可以看出结果是我们想得到的正确数据。

  • 字典特性实现多级评论

仅限于python,其他语言还是使用递归方法吧!

当我们使用这个方法后,就觉得使用递归不是太low,而是Python没有学好的节奏!此法利用的就是Python的基本内存地址引用原理,只需要创建一个额外的字典,然后调用get()方法即可!

啥也别说了,直接上代码:

先来个性能比较差的版本:

def comment(request):
obj = models.Comment.objects.values()
comment_tree = []
for item in obj:
item.update({'children': []})

for item in obj:
current_item = item
current_item_parent_id = current_item['parent_id']
if not current_item_parent_id:
comment_tree.append(item)
else:
for data in obj:
if data['id'] == current_item_parent_id:
data['children'].append(item)
return HttpResponse(json.dumps(comment_tree))

数据结果:

这个方法确实比上面的递归要好很多,但是有一个很明显的问题,每次都要重新去遍历一次原始数据,很是影响性能啊!

高性能代码,也就是前面说的构建新字典,调用get()方法:

def comment(request):
obj = models.Comment.objects.values()
comment_tree = []
comment_tree_dict = {}
for item in obj:
item.update({'children': []})
comment_tree_dict[item['id']] = item

for item in obj:
parent_item = comment_tree_dict.get(item['parent_id'])
if not parent_item:
comment_tree.append(item)
else:
parent_item['children'].append(item)
return HttpResponse(json.dumps(comment_tree))

数据结果:

用在线JSON转换工具得到的结果是一致的!

3. 数据展示

在前端进行数据的展示有两种方式:

  • 后端模板处理
  • 前端处理

3.1 前端处理

前端对数据进行处理,我需要使用JavaScript,推荐用原生语句来写,并不推荐使用jQuery(行业里流传这么一句话:比较老的程序员在使用jQuery),反正我不会!

当然,在前端是有好处的,将服务器压力分布到每个浏览器!

这里我们使用Ajax进行接收数据,然后html进行渲染,CSS进行修饰。

3.1.1 样式模板构建

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
h1{
text-align: center;
}
.box{
margin-left: 20px;
}
.data-show{
margin-left: 15px;
}
</style>
</head>
<body>
<h1>这里是多级评论演示!</h1>
<p>*************************************************************</p>
<p>文章文章文章文章文章文章文章文章文章文章文章文章文章文章文章</p>
<p>*************************************************************</p>
<span><b>评论</b></span>
<div>
<div class="box">
<a new_id="3" class="data-show">评论一</a>
</div>
<div class="box">
<a new_id="2" class="data-show">评论一</a>
</div>
<div class="box">
<a new_id="1" class="data-show">评论一</a>
</div>
</div>
</body>
</html>

现在开始书写Ajax,我们通过使用DOM0级进行事件绑定:

仅供学习参考:懒执事 » Django022-打造通用多级评论模型

分享到:更多 ()