# data=[ # [11,22,33], # [44,55,66] # ] # data[0].append(data[1]) # print(data) # data[1].append(77) # # 由于[11, 22, 33, [44, 55, 66, 77]]和[[11, 22, 33, [44, 55, 66, 77]], [44, 55, 66, 77]] # # 引用的是同一块内存地址,所以谁添加了内容对应的也跟随添加 # data=[ # {'k1':'v1'}, # {'k2':'v2'} # ] # # for item in data: # item['kk']='vv' # # # # python里面列表和字典引用的是引用类型,当我们拿到地址被操作了,会发生级联变化;如果想要再独立一份就需要copy() # # msg_list = [ # {'id':1,'content':'xxx','parent_id':None}, # {'id':2,'content':'xxx','parent_id':None}, # {'id':3,'content':'xxx','parent_id':None}, # {'id':4,'content':'xxx','parent_id':1}, # {'id':5,'content':'xxx','parent_id':4}, # {'id':6,'content':'xxx','parent_id':2}, # {'id':7,'content':'xxx','parent_id':5}, # {'id':8,'content':'xxx','parent_id':3}, # ] # # v=[row.setdefault('child') for row in msg_list] # # # # #注意列表生成公式里面 不能出现=等赋值操作,可以使用方法 # # print(msg_list) # # #方式2 # # msg_list_dict={} # # msg_list_dict={ # # 1, {'id':1,'content':'xxx','parent_id':None}, # # 2, {'id':2,'content':'xxx','parent_id':None}, # # } # #字典的在存储的时候,键会转换成一个hashs值,这个hash值和内存地址相关,# 无论字典里的键值对有多大,都可以一次的寻找到key对应的value # #字典查询就是数据库里的hansh 索引,直接拿到key就可以快得惊人的获取到value # #无需向列表那样 查询 是 一个个顺序 遍历 # # #所以定义一个字典 方便我们查找 评论信息,而且因为Python是数据引用类型,相同数据引用一块内存,也不会占用内存 # for item in msg_list: # item['child']=[] # msg_list[item['id']]=item # # #程序运行到此处,会有两个msg_list msg_list_dict # ret=[] # for item in msg_list: # pid=item['parent_id'] # if pid: # msg_list[pid]['child'].apend(item) # else: # ret.append(item) msg_list = [ {'id':1,'content':'xxx','parent_id':None}, {'id':2,'content':'xxx','parent_id':None}, {'id':3,'content':'xxx','parent_id':None}, {'id':4,'content':'xxx','parent_id':1}, {'id':5,'content':'xxx','parent_id':4}, {'id':6,'content':'xxx','parent_id':2}, {'id':7,'content':'xxx','parent_id':5}, {'id':8,'content':'xxx','parent_id':3}, ] msg_dict={} #1、给所以 评论设置一个 child键=[空列表的值] [row.setdefault('child',[]) for row in msg_list] 列表生成器方法 #3、定义一个查询用的字典 for item in msg_list: item['child']=[] msg_dict[item['id']]=item #2、把带有'parent_id'的键的字典,添加到对应的父字典里(你会想到再次遍历msg_list,但是多层循环效率低下,于是重新定义一个字典) for item in msg_list: pid=item['parent_id'] if pid: msg_dict[pid]['child'].append(item) # 4、找出根评论· root_coment=[] for item in msg_list: if not item['parent_id']: root_coment.append(item) for item in root_coment: print(item)
模板
''' <div class="cooment"> <div class="content"></div> 根评论 <div class="cooment"> <div class="content"></div> 子评论 <div class="content"></div> 子评论 <div class="content"></div> 子评论 </div> <div class="content"></div> 根评论 <div class="cooment"> <div class="content"></div> 子评论 <div class="content"></div> 子评论 <div class="content"></div> 子评论 </div> <div class="content"></div> 根评论 <div class="content"></div> </div> ''' #---------------------------------------------- comment_str='' comment_str += '<div class="comment">' for row in result: tp1='<div class="content">%s</div>'% (row['content']) comment_str+=tp1 if row['child']: for chil in row['child']: comment_str += '<div class="comment">' tp1 = '<div class="content">%s</div>' % (chil['content']) comment_str += tp1 comment_str += '</div>' comment_str += '</div>' return render(request,'comment.html',locals())
多级评论之博客园结构版
思路
1、评论区和评论展示区域
<form action="/comment/" method="post" novalidate> {% csrf_token %} {{ obj.content }} <span id="errors"></span> 评论展示区域 <p> <input type="button" value="发表" class="btn btn-primary ajax_comment"> 评论发表区域 </p> </form>
2、评论在评论发表区域属于之后使用ajax提交到server存储数据库
<script> $('.ajax_comment').click(function () { {# 由于评论内容前缀携带@父评论\n,所以需要 找到'\n'的字符串索引进行截断#} var $index = $('[name="content"]').val().indexOf('\n') var $content = $('[name="content"]').val().substr($index + 1) var $csrf = $("[name='csrfmiddlewaretoken']").val(); var $article_id = {{ artic.nid }}; var formdata = new FormData(); formdata.append('csrfmiddlewaretoken', $csrf) formdata.append('content', $content) formdata.append('article_id', $article_id) formdata.append('parent_id_id', $prant_comment_id) {% if request.user.is_authenticated %} $.ajax({ url: '/comment/', type: 'post', data: formdata, processData: false, contentType: false, success: function (data) {
3、server存储完成后,把成功消息回传浏览器
def comment(request): responses={'flag':True,'msg':None} obj=Commenform(request.POST) if obj.is_valid(): content=obj.cleaned_data['content'] article_id=request.POST.get('article_id') user_id=request.user.nid parent_id_id=request.POST.get('parent_id_id',) comment_obj=models.Comment.objects.create(article_id=article_id, content=content, parent_id_id=parent_id_id, user_id=user_id, ) responses['create_time']=str(comment_obj.create_time)[:16] models.Article.objects.filter(nid=article_id).update( comment_count=F('comment_count')+1) else: responses['flag']=False responses['msg']=obj.errors return HttpResponse(json.dumps(responses))
4、此时数据库中已有数据,通过页面刷新即可获得最新评论,可是体验不佳,于是浏览器可以通过 server端回传的信号,使用JavaScript或jQuery动态添加评论标签,制造显示效果;
if (data['flag']) { var $create_time = data['create_time']; var $content = $('[name="content"]').val(); s = '<li class="list-group-item comment_item"><a href="">{0}</a><a href="">{1}</a><a href="" ' + 'class="pull-right"> 支持</a> <a href="#comment_content" class="pull-right reply_btn">' + '回复</a><span class="{2}"></span> <div> <span>{3}</span> <p>{4}</p></div> </li>' s = s.format( '{{ request.user.username }}', $create_time, $prant_comment_id, $fathercomment_username, $content ); $('.comment_list').append(s) $('[name="content"]').val(' '); $prant_comment_id = ''; $fathercomment_username = ''; } else { $.each(data['msg'], function (i, j) { $('#errors').text(j[0]).css('color', 'red') setTimeout(function () { $("#errors").text("") }, 1000) }) } } })
5、根评论搞定(对文章的评论),可是如果涉及对评论进行评论呢?利用A标签的锚特效, 当用户点击回复的A标签,同样锚定到评论区域‘’
6、于此同时 给回复A标签绑定其他事件,携带 父级评论的ID、内容
<script> var $prant_comment_id = ''; var $fathercomment_username = ''; $(".comment_list").on("click", "#reply_btn", function () { var $fathercomment_username = $(this).siblings().eq(0).text() $('#comment_content').val('@' + $fathercomment_username + '\n') $prant_comment_id = $(this).siblings().eq(3).attr('name') }) </script>
7.示意图
8.Golang实现
在Go语言中复杂的数据类型需要使用Struct进行数据封装;
appList := make([]*AppInfo, 0) type AppInfo struct { pname string name string children []*AppInfo //也需要设置为指针类型 }
但是结构体属于值类型,如果需要对[]切片中结构体进行修改需要使用指针类型;
package main import ( "fmt" "strings" ) type AppInfo struct { pname string name string children []*AppInfo } func (this *AppInfo) AddChidren(child *AppInfo) { this.children = append(this.children, child) } // 通过Map的key唯一的特性过滤重复元素 func RemoveDuplicate(applist []*AppInfo) []*AppInfo { result := []*AppInfo{} tempMap := make(map[string]bool) for _, app := range applist { if ok := tempMap[app.name]; !ok { result = append(result, app) tempMap[app.name] = true } } return result } func makeTree(applicationList []string) { /* security/webaduit/http-proxy security/LogProxy/日志审计采集器 */ appList := make([]*AppInfo, 0) appMap := make(map[string]*AppInfo, 0) //组织树形结构 for _, appInfo := range applicationList { splitedList := strings.Split(appInfo, "/") rootName := splitedList[0] parentName := splitedList[1] name := splitedList[2] children := []*AppInfo{} rootRow := &AppInfo{"", rootName, children} parentRow := &AppInfo{rootName, parentName, children} currentRow := &AppInfo{parentName, name, children} appList = append(appList, rootRow, parentRow, currentRow) } //去重 appList = RemoveDuplicate(appList) //建立查询索引 for _, app := range appList { appMap[app.name] = app } //组织树形结构 for _, currentApp := range appList { //获取当前App的父亲节点 pname := currentApp.pname if pname != "" { //从查询appMap 中查询到当前APP的父级是谁? prantApp := appMap[pname] //把当前App信息追加到当前APP父级的childList prantApp.AddChidren(currentApp) } } //展示组织好的树形结构 for _, app1 := range appList { if app1.pname == "" { fmt.Println("---------level1---", app1.name) for _, app2 := range app1.children { fmt.Println("---------level2-----", app2.name) for _, app3 := range app2.children { fmt.Println("---------level3---------", app3.name) } } } } } func main() { treeData := []string{"北京/朝阳/CBD", "北京/海淀/西二旗"} makeTree(treeData) }