写一个函数,输入整数n,能够输出斐波那契数列中第n个的值。
2. 分析层级评论
从现象分析:
我们可以把每一条评论抽象为字母,为了方便识别,我们约定首层评论为A,B , C,D之类不带数字的字母。
ABCD的直接子评论为A1,A2,B1,B2之类的。A1 A2,B1, B2的子评论为A1-1, A1-2,B1-1,B1-2。。A1-1的子评论为A1-1-1,A1-1-2等等以此类推
那么我们可以简单的抽象出一个例子,类似这样:
我们的每个post的评论列表是一个queryset,类似如下:
<QuerySet [<Comment: A>, <Comment: A1>, <Comment: A2>, <Comment: A1-1>, <Comment: A2-1>, <Comment: A1-1-1>, <Comment: A2-1-1>, <Comment: B>, <Comment: B1>, <Comment: A1-1-2>, <Comment: B2>, <Comment: C>]>
现在问题转换为,如何将上面这样的queryset显示成上图中的层级关系。
进一步分析发现,上图中的层级关系如果用python的字典来表示,可以表示为:
{
A : {A1:{A1-1:{A1-1-1:{},A1-1-2:{}}}, A2:{A2-1:{A2-1-1:{}}}},
B: {B1: {}, B2: {}},
C: {}
}
每一行其实可以找到一对关系:当前节点和父节点的关系,比如第一行父节点为None,子节点为A。第二行父节点为A,子节点为A1.那么上图的关系可以转换为一个列表:
[(None, A),
(A, A1),
(A1, A1-1),
(A1-1, A1-1-1),
(A1-1, A1-1-2),
(A,A2),(A2, A2-1),
(A2-1,A2-1-1),
(None,B),
(B, B1),
(B,B2),
(None,C)]
我们的评论列表,每个评论对象都包含自己的父评论,即存在这样一种关系(comment_obj.parent_comment, comment_obj),那么我们可以把评论的queryset转换为类似上面的列表。如果可以找到把上面列表转换为层级显示的字典的话,即可以实现我们评论列表queryset的层级评论显示。
基本思路:
创建一个空字典,用来保存将评论列表转换为评论字典的结果。
如果一个评论没有父节点,那么将这个评论作为根节点,存如评论字典。如果有父节点,那么递归去查找父节点,如果找到了父节点,将自己插入父节点下面。正确生成评论字典之后,再对字典进行递归输出即可。我们将输出字典的函数自定义为一个simple_tag,方便我们在前端调用。
代码如下:
from django.utils.safestring import mark_safe
# 递归查找父节点
def find_father(dic, comment_obj):
# 对字典中的每一组元素进行循环操作
for k, v_dic in dic.items():
# 如果k等于comment_obj的父节点,那么表示找到了父亲。
if k == comment_obj.parent_comment:
# 找到了父亲,认祖归宗,把自己归位到父亲下面,并给将来的儿子留个位置
dic[k][comment_obj] = {}
# 找到了父亲,处理完毕,返回
else:
# 刚才没找到,剥一层,接着往下找。
find_father(dic[k], comment_obj)
# 递归生成html字符串
def generate_comment_html(sub_comment_dic, margin_left_val):
# 先创建一个空字符串
html = ""
# 对传入的字典进行循环操作
for k, v_dic in sub_comment_dic.items():
html += "<div style='margin-left:%spx'><span class='nickname'>" % margin_left_val + k.name + "</span>" + "<time class='submit-date'>" + str(k.created_time.strftime('%Y-%m-%d %H:%M:%S')) + "<div class='text'>" + k.text + '</div></div><hr>'
# html += "<div style='margin-left:%spx' class='comment-node'>" % margin_left_val + k.text + "</div>"
# 有可能v_dic中依然有元素, 递归继续加
if v_dic:
html += generate_comment_html(v_dic, margin_left_val+35)
# 循环完成最后返回html
return html
# 生成层级评论
@register.simple_tag
def build_comment_tree(comment_list):
# 定义一个空字典用来保存转换之后的结果
comment_dic = {}
# 对comment_list中的每个元素进行循环
for comment_obj in comment_list:
# 判断comment_obj是否存在父节点。如果没有,这把该评论作为第一个节点
if comment_obj.parent_comment is None:
comment_dic[comment_obj] = {}
else:
# 否则去找该对象的父节点。
find_father(comment_dic, comment_obj)
# 上面执行完毕,comment_dic中会有转换好的结果
# 开始拼接html字符串
html = "<ul class='comment-list list-unstyled'>"
# 规定一个margin left,每次有递归的时候就往右缩进一点。
margin_left = 0
# 对comment_dic中的每一组元素进行操作
for k,v in comment_dic.items():
# 第一层html
html += "<li class='comment-item'><span class='nickname'>" + k.name + "</span>" + "<time class='submit-date'>" + str(k.created_time.strftime('%Y-%m-%d %H:%M:%S')) + "<div class='text'>" + k.text + '</div></li>'
# 通过递归把他的儿子加上
html += generate_comment_html(v, margin_left+35)
# 最后把ul关上
html += " </ul>"
# 关掉转义
return mark_safe(html)