博客系统-评论or评论树
url配置
url(r'^commentTree/(?P<article_id>\d+)/',views.commentTree), url(r'^(?P<username>.*)/com', views.com),
视图:
def commentTree(request,article_id): # 找到当前文章对应的所有的评论 comment_list = models.Comment.objects.filter(article_id=article_id).values("nid","content","parent_comment_id","user__username") comment_dict = {} for comment in comment_list: comment["chidren_commentList"] = [] #添加一个字段 comment_dict[comment["nid"]] = comment #创建一个字典,字典的键为nid,值为评论 commentTree = [] for comment in comment_list: pid = comment.get("parent_comment_id") #获取到父级评论的iD if pid: comment_dict[pid]["chidren_commentList"].append(comment) #吧该条记录添加到字典中 --------父级评论 else: commentTree.append(comment) #没有获取到说明是根评论 print("==============>",comment_dict) return HttpResponse(json.dumps(commentTree)) #串过去的是一个具有父子结构关系的字典
def com(request,username): current_user = request.user article_id = request.POST.get("article_id") # 当前的文章ID content = request.POST.get("text") # 当前的评论内容 print("===============content>",content) user_id = request.user.nid parent_comment_id = request.POST.get("parent_comment_id") response = {"success":False} print("===========>可以走到这里") if request.POST.get("parent_comment_id"): #可以去到父级ID,为父级的评论 with transaction.atomic(): comment_obj = models.Comment.objects.create(content=content, user_id=user_id, article_id=article_id, parent_comment_id=parent_comment_id) response["success"] = True response["parent_comment_username"]=comment_obj.parent_comment.user.username response["parent_comment_content"]=comment_obj.parent_comment.content response["comment_id"] = comment_obj.nid else: #这里娶不到,为根评论 print("===============>根评论") with transaction.atomic(): comment_obj= models.Comment.objects.create(content=content, user_id=user_id, article_id=article_id) response["success"] = True response["comment_id"] = comment_obj.nid return HttpResponse(json.dumps(response))
前端页面
{% extends "homeSite.html" %} {% block content %} <div class="article_region"> <div class="row"> {# 文章的主体区域#} <h3 class="title">{{ article_obj.title }}</h3> <hr> <div class="article_con">{{ article_obj.articledetail.content|safe }}</div> </div> <hr> <div class="info pull-right"> <div class="row "> {# 小按钮#} {{ current_user }} 发表于 <span>{{ article_obj.create_time|date:"Y-m-d" }}</span> <span>评论({{ article_obj.comment_count }})</span> <span>点赞({{ article_obj.up_count }})</span> <span>阅读({{ article_obj.read_count }})</span> <a href="">编辑</a> <a href="">收藏</a> </div> <br> </div> <br> <br> <br> <hr> {# 文章的分类和标签展示#} <p>分类:<a href="">{{ current_blog.category_set.first.title }}</a></p> <p>标签:{% for tag in current_blog.tag_set.all %} <a href="">{{ tag.title }}</a>| {% endfor %} </p> <div class="updown row"> {# 点赞块#} <div class="author_profile"> <div id="author_profile_info" class="author_profile_info"> <a href="" target="_blank"><img src="{{ current_user.avatar.url }}" width="50" height="50" alt="" class="author_avatar"></a> <div id="author_profile_detail" class="author_profile_info"> {# 头像快#} <a href="">{{ current_user }}</a><br> <a href="">关注--{{ current_user.fans.user }}</a><br> <a href="">粉丝--{{ current_user.fans.follower }}</a><br> </div> </div> </div> <div class="buryit pull-right"> <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="diggit pull-right"> <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> </div> </div> <span class="diggnum_error pull-right"></span> {# 错误提示#} <hr> <div></div> <hr> <div class="comment_count"> <h5>评论树</h5> <div class="comment_tree_list"> </div> <p>评论列表:</p> <div class="contcont"> 所有的评论楼层 {% for comment in comment_obj_list %} <div class="row pl"> <div class="col-md-12"> <div class="row"> <div class="pull-left"> # <div class="nb" style="display: inline-block"> {{ forloop.counter }}</div> 楼 {{ comment.create_time }} <a href="/blog/{{ comment.user.username }}" class="sp">{{ comment.user.username }}</a> </div> <a href="" class="sendMsg2This"></a> <div class="pull-right"> {% if request.user.username %} <a class="reply" id="reply" comment_id="{{ comment.nid }}" comment_username="{{ comment.user.username }}">回复</a> {% else %} {% endif %} <a>引用</a> {% if request.user.username %} <a>删除</a> {% else %} {% endif %} </div> </div> <div style="background-color: white"> {% if comment.parent_comment_id %} @<a href="">{{ comment.parent_comment.user.username }} {{ comment.parent_comment.content|safe }} </a> {% endif %} </div> <div class="cont">{{ comment.content|safe }}</div> <br> </div> </div> {% endfor %} </div> <hr> <div class="row pull-right"> {# 模仿博客园的,具体功能慢慢扩展#} <a href="">刷新评论</a> <a href="">刷新页面</a> <a href="">返回顶部</a> </div> <br> </div> <div class="had_comment_region"> {# 发表评论的框框,暂时不太好看#} <div id="commentform_title">发表评论</div> <span id="tip_comment" style="color: red"></span> <p><input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容:</p> <form> {% csrf_token %} <textarea id="comment_content" class="comment_content" name="content" style="width:500px;height:300px;"></textarea> </form> <button class="submit" >提交</button> </div> </div> {# 辅助标签#} <div class="infos" user_username="{{ request.user.username }}" article_id="{{ article_obj.nid }}"></div> <script> {# KindEditor.options.filterMode = false;#} KindEditor.ready(function (K) { window.editor = K.create('#comment_content', { width: "500px", height: "300px", resizeType: 0, {# filterMode: false,#} extraFileUploadParams: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, items: [ 'undo', 'redo', 'copy', 'paste', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline', 'strikethrough', 'hr', 'emoticons' ], }); }) </script> <script> {# 建楼层用的#} var nb = parseInt($(".nb:last").text()) + 1; {# 格式化时间#} Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小时 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; }; var createTime = new Date().Format("yyyy-MM-dd hh:mm"); {# 提交评论#} $(".submit").click(function () { if (editor.html().charAt(0) != "@"){parent_comment_id = null} if (parent_comment_id) { {# 这里是直接回复回复#} var index = editor.html().indexOf("\n") cont = editor.html().slice(index + 1) {# alert(index)#} {# alert(cont)#} {# 截取换行符第一次出现的地方的下一个点到结束 #} } else { cont = editor.html(); } $.ajax({ url: "/blog/{{ current_user }}/com/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }}, text: cont, parent_comment_id: parent_comment_id }, success: function (data) { data = JSON.parse(data); if (data["success"]) { {# if有父级评论#} if (parent_comment_id) { var tr = '<div class="row pl">\ <div class="col-md-12">\ <div class="row">\ <div class="pull-left">\ # ' + {{ forloop.counter }} + ' 楼 ' + createTime + ' <a class="sp" href="">{{ user_obj }}</a>\ </div>\ <a href="" class="sendMsg2This"></a>\ <div class="pull-right"><a class="reply" id="reply" comment_id="' + data.comment_id + '" comment_username="' + data.parent_comment_username + '" >回复</a> <a >引用</a> <a >删除</a></div>\ </div>\ <div style="background-color: white">@<a href="">' + data.parent_comment_username + '\ ' + data.parent_comment_content + '</a></div>\ <div class="cont">' + cont + '</div>\ <br>\ </div>\ </div>'; } else { var tr = '<div class="row pl">\ <div class="col-md-12">\ <div class="row">\ <div class="pull-left">\ # ' + {{ forloop.counter }} + ' 楼 ' + createTime + ' <a class="sp" href="">{{ user_obj }}</a>\ </div>\ <a href="" class="sendMsg2This"></a>\ <div class="pull-right"><a class="reply" id="reply" comment="' + data.comment_id + '" comment_username="{{ request.user.username }}" >回复</a> <a >引用</a> <a >删除</a></div>\ </div>\ <div class="cont">' + cont + '</div>\ <br>\ </div>\ </div>'; } $(".contcont").append(tr); alert(cont) editor.html(""); parent_comment_id = null; } else { location.href = "/login/" } } }) }); function foo() { $(".diggnum_error").html("") } {# 点赞踩踩,每次进来都会写cookie,前端获取到执行响应的操作#} $(".diggit").click(function () { if ($(".infos").attr("user_username")){ $.ajax({ url: "/blog/up_count/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }} }, success: function (data) { data = JSON.parse(data); if (data["state"]) { var val = parseInt($("#digg_count").html()) + 1; $("#digg_count").html(val) } else { $(".diggnum_error").html("请不要重复点赞").css("color", "red"); setTimeout(foo, 3000) } } }) }else{ location.href="/login/" } }); $(".buryit").click(function () { if ($(".infos").attr("user_username")){ $.ajax({ url: "/blog/down_count/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }} }, success: function (data) { data = JSON.parse(data); {# console.log(data);#} if (data["state"]) { var val = parseInt($("#bury_count").html()) + 1; $("#bury_count").html(val) } else { $(".diggnum_error").html("去你大爷的,").css("color", "red") setTimeout(foo, 3000) } } }) }else{ location.href="/login/" } }); {# 回复按钮功能#} {# var parent_comment_id = null;#} {# $(".contcont").on("click", ".reply", function () {#} {# var parent_user_username = $(this).attr("comment_username");#} {# editor.sync();#} {# editor.focus();#} {# editor.appendHtml("@" + parent_user_username + "<br>")#} {# parent_comment_id = $(this).attr("comment_id");#} {# alert(parent_comment_id)#} {##} {# })#} $(".comment_tree_list").on("click", ".reply", function () { var parent_user_username = $(this).attr("comment_username"); editor.sync(); editor.focus(); editor.appendHtml("@" + parent_user_username + "<br>") parent_comment_id = $(this).attr("comment_id"); alert(parent_comment_id) }) {# 获取到评论树#} $.ajax({ url: "/blog/commentTree/{{ article_obj.nid }}/", success: function (data) { var data = JSON.parse(data); var s = showCommentTree(data); $(".comment_tree_list").append(s); } }) {# 评论树相关样添加式慢慢#} var numb = 0; function showCommentTree(comment_list) { var html = ""; numb +=1; $.each(comment_list, function (i, comment_dict) { var count = comment_dict["content"]; var user = comment_dict["user__username"]; var parent_comment_id = comment_dict["parent_comment_id"]; var createTime = new Date().Format("yyyy-MM-dd hh:mm"); var comment_str = '<div class="comment media offset"><div class="content" >' + '<div class="row" style="border: 1px dashed red">' + '<div class="col-md-7 pull-left">#<div class="nbv" style="display: inline-block">'+numb+'</div>楼'+createTime+'__'+user+'<a href="" class="sendMsg2This"></a></div>' + '<div class="col-md-5 pull-right"><a class="reply" id="reply" comment_id='+parent_comment_id+' comment_username='+user+'>回复</a> <a>引用</a> <a>删除</a> </div>' + '</div><div class="row" style="border: 1px dashed blue">'+count+'</div></div>'; {# var comment_str = '<div class="media offset"><div class="media-left "><a href="/blog/' + comment_dict["user__username"] +#} {# '/"><img class="media-object" src="/media/' + comment_dict["user__avatar"] + '" width="30" height="30" alt="..."></a></div>' +#} {# '<div class="media-body"><div class="media-heading"><span>#' + comment_dict["nid"] + '楼</span><span> ' +#} {# comment_dict["comment_data"] + '</span><a href="/blog/' + comment_dict["user__username"] + '/"><span> ' +#} {# comment_dict["user__username"] + '</span></a><a class="reply_comment_btn pull-right" comment_id="' + comment_dict["nid"] +#} {# '" conmment_username="' + comment_dict["user__username"] + '">回复</a></div><div class="content_box"><div class="c_content">' + comment_dict["content"] +#} {# '</div><div class="comment_vote"></div></div></div>';#} {##} {# 如果他有子列表,继续执行该函数#} if (comment_dict["chidren_commentList"]) { var s = showCommentTree(comment_dict["chidren_commentList"]); comment_str += s; } comment_str += "</div>"; html += comment_str; {# alert(html)#} }); return html } </script> {% endblock %} {% block page %} {% endblock %} <script> {# 建楼层用的#} var nb = parseInt($(".nb:last").text()) + 1; {# 格式化时间#} Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小时 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; }; var createTime = new Date().Format("yyyy年MM月dd日 hh:mm"); {# 提交评论#} $(".submit").click(function () { if ($("#comment_content").val().charAt(0) != "@") { parent_comment_id = null } if (parent_comment_id) { {# 这里是直接回复回复#} alert("评论回复"); {# var index = $("#comment_content").val().indexOf("\n");#} var index = editor.html().indexOf("\n") alert(index) {# 找到换行符第一次出现的地方 #} {# cont = $("#comment_content").val().slice(index + 1);#} cont = editor.html().slice(index+1) {# 截取换行符第一次出现的地方的下一个点到结束 #} alert(cont) } else { {# 这里只是单纯的回复#} alert("单纯的回复"); {# cont = $("#comment_content").val()#} cont = editor.html() } {# 当前输入的内容#} $.ajax({ url: "/blog/{{ current_user }}/com/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }}, text: cont, parent_comment_id: parent_comment_id, }, success: function (data) { data = JSON.parse(data); if (data["success"]) { {# if有父级评论#} if (parent_comment_id) { var tr = '<div class="row pl">\ <div class="col-md-12">\ <div class="row">\ <div class="pull-left">\ # ' + nb + ' 楼 ' + createTime + ' <a class="sp" href="">{{ user_obj }}</a>\ </div>\ <a href="" class="sendMsg2This"></a>\ <div class="pull-right"><a class="reply" id="reply" comment_id="' + data.comment_id + '" comment_username="' + data.parent_comment_username + '" >回复</a> <a >引用</a> <a >删除</a></div>\ </div>\ <div style="background-color: white">@<a href="">' + data.parent_comment_username + '\ ' + data.parent_comment_content + '</a></div>\ <div class="cont">' + cont + '</div>\ <br>\ </div>\ </div>'; } else { var tr = '<div class="row pl">\ <div class="col-md-12">\ <div class="row">\ <div class="pull-left">\ # ' + nb + ' 楼 ' + createTime + ' <a class="sp" href="">{{ user_obj }}</a>\ </div>\ <a href="" class="sendMsg2This"></a>\ <div class="pull-right"><a class="reply" id="reply" comment="' + data.comment_id + '" comment_username="{{ request.user.username }}" >回复</a> <a >引用</a> <a >删除</a></div>\ </div>\ <div class="cont">' + cont + '</div>\ <br>\ </div>\ </div>'; } $(".contcont").append(tr); $(".comment_content").val(""); parent_comment_id = null; } else { alert($.session.get("urls")); location.href = "/login/" } } }) }); function foo() { $(".diggnum_error").html("") } {# 正常用户#} $(".diggit").click(function () { $.ajax({ url: "/blog/up_count/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }} }, success: function (data) { data = JSON.parse(data); if (data["state"]) { var val = parseInt($("#digg_count").html()) + 1; $("#digg_count").html(val) } else { $(".diggnum_error").html("请不要重复点赞").css("color", "red"); setTimeout(foo, 3000) } } }) }); $(".buryit").click(function () { $.ajax({ url: "/blog/down_count/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id:{{ article_obj.nid }} }, success: function (data) { data = JSON.parse(data); {# console.log(data);#} if (data["state"]) { var val = parseInt($("#bury_count").html()) + 1; $("#bury_count").html(val) } else { $(".diggnum_error").html("去你大爷的,").css("color", "red") setTimeout(foo, 3000) } } }) }); {# 游客进来之后吧当前的url写到session中,跳转到login页面#} $("#wu_diggnum").click(function () { $.session.set("urls", window.location.href); alert(window.location.href) location.href = "/login/" }); {# 回复按钮功能#} var parent_comment_id = null; $(".contcont").on("click", ".reply", function () { {# alert(parent_comment_id);#} var parent_user_username = $(this).attr("comment_username"); alert(parent_user_username) {# location.href = "#comment_content";#} {# $(".comment_content").focus();#} editor.focus() {# $(".comment_content").val("@" + parent_user_username + "\n");#} editor.html("@" + parent_user_username + "\n") parent_comment_id = $(this).attr("comment_id"); alert(parent_comment_id) {# alert(parent_comment_id);#} }) {# 获取到评论树#} $.ajax({ url: "/blog/commentTree/{{ article_obj.nid }}/", success: function (data) { var data = JSON.parse(data); var s = showCommentTree(data); $(".comment_tree_list").append(s); } }) function showCommentTree(comment_list) { var html = ""; $.each(comment_list, function (i, comment_dict) { var val = comment_dict["content"]; var comment_str = '<div class="comment"><div class="content"><span>' + val + '</span></div>'; if (comment_dict["chidren_commentList"]) { var s = showCommentTree(comment_dict["chidren_commentList"]); comment_str += s; {# alert(comment_str)#} } comment_str += "</div>"; html += comment_str; {# alert(html)#} }); return html } </script>
css相关样式
.title{
color: #2aabd2;
}
.article_region .article_con{
margin-left: 20px;
}
.updown .diggit{
width: 46px;
height: 52px;
background: url("/static/img/upup.gif") no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.updown .buryit{
margin-left:20px;
width: 46px;
height: 52px;
background: url("/static/img/downdown.gif") no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
/*.updown{*/
/*margin-left: 0;*/
/*}*/
.subComment_region #tbCommentAuthor{
background-position: 3px -3px;
background-image: url("/static/img/icon_form.gif");
background-repeat: no-repeat;
border: 1px solid #ccc;
padding: 4px 4px 4px 30px;
width: 300px;
font-size: 13px;
}
#commentform_title {
background-image: url("/static/img/icon_addcomment.gif");
background-repeat: no-repeat;
padding: 0 0 0 25px;
margin-bottom: 10px;
}
.author_avatar{
margin-left: 20px;
}
.had_comment_region input.author{
background-image: url("/static/img/icon_form.gif");
background-repeat: no-repeat;
border: 1px solid #ccc;
padding: 4px 4px 4px 30px;
width: 300px;
font-size: 13px;
}
#author_profile {
float: left;
width: 280px;
margin-top: 0;
margin-bottom: 10px;
color: #000;
margin-left: 0;
font-size: 12px;
}
.author_profile_info {
float: left;
line-height: 18px;
}
div {
display: block;
}
.author_avatar {
vertical-align: top;
float: left;
margin-right: 5px;
padding-top: 5px;
padding-left: 2px;
border: 0;
}
.author_profile_info {
float: left;
line-height: 18px;
}
.author_profile .author_profile_info .author_profile_detail a{
border-bottom: 2px dotted #333;
color: #000;
text-decoration: none;
}
.sendMsg2This:link, .sendMsg2This:visited, .sendMsg2This:active {
font-size: 12px;
text-decoration: none;
background: url("/static/img/icoMsg.gif") no-repeat top left;
padding-left: 20px;
}
.feedbackListSubtitle a:hover {
color: #f60;
text-decoration: none;
}
.sendMsg2This:hover {
background: url("/static/img/icoMsg.gif") no-repeat bottom left;
}
.sp:hover{
color: #f60;
text-decoration: none;
}
.sp {
color: #666;
font-weight: normal;
}
.pl{
margin-left: 0;
margin-top: 3px;
border-bottom: 1px solid #EDD9B8;
}
.comment{
margin-left: 20px;
}
本文来自博客园,作者:一石数字欠我15w!!!,转载请注明原文链接:https://www.cnblogs.com/52-qq/p/8669499.html