BBS项目文章评论功能

BBS项目文章评论技术技术点分析

前端

  • 允许根评论和子评论(评论评论的评论),可以评论自己的文章。
  • 用户未登录不能评论且隐藏评论输入框(request.user.is_authenticated)。
  • 评论内容有两种渲染方式:
    • 刷新页面时,从后端取出评论数据,前端循环展示
    • 评论后DOM操作临时将评论内容渲染到评论列表,使用的是js的模版字符串语法。
  • 根评论朝后端提交的数据:文章主键、评论内容、
  • 子评论朝后端提交的数据:文章主键、评论内容、父评论主键
  • 获取父评论的方式:给回复按钮绑定一个自定义属性,属性值为父评论主键
  • 区分子评论和根评论关键在于是否有父评论,这里面为了统一,提交根评论时也携带父评论(只不过值为null,因为数据库该字段支持为空)。

数据库

  • 评论表的字段:评论人、文章、评论时间、内容、自关联字段(实现子评论)

后端

  • 需要登录后才能评论,所以使用一个登录校验装饰器

  • 后端逻辑比较简单,接收评论内容、文章主键、父评论主键

  • 评论内容为空值,响应提示信息

  • 使用事物同时更新文章表和评论表。

代码

前端

{#    评论列表展示区start#}
<div>
    <h5>评论列表</h5>
    <hr>
    <ul class="list-group" id="comment_list">
        {#    #1楼 2020-05-06 19:04 立志做一个好的程序员#}
        {% for comment in comment_list %}
        <li class="list-group-item">
            <span>#{{ forloop.counter }}楼&nbsp;</span>
            <span>{{ comment.comment_time|date:'Y-m-d H:i'}}&nbsp;</span>
            <span><a href="/{{ comment.user }}/">{{ comment.user }}</a></span>
            {#   自定义属性parentid,获取父评论主键#}
            <span class="pull-right"><a class="btn_reply" parentid="{{ comment.pk }}" replyto="{{ comment.user }}">回复</a></span>
            <p class="text-success">
                {% if comment.parent %}
                <span>&nbsp;&nbsp;&nbsp;&nbsp;@{{ comment.parent.user }}</span><br>
                {% endif %}
                <span>&nbsp;&nbsp;&nbsp;&nbsp;{{ comment.content }}</span>
            </p>
        </li>
        {% endfor %}
    </ul>
</div>
{#    评论列表展示区end#}

{#    评论区start#}
{% if request.user.is_authenticated %}
<div id="comment_zone">
    <h5 class="glyphicon glyphicon-comment">发表评论</h5>
    <div class="form-group">
        <textarea name="" class="form-control" id="comment_body" cols="60" rows="10"></textarea>
    </div>
    <div class="form-group">
        <button class="btn btn-primary" id="btn_comment_submit">提交评论</button>
        <span style="color: red; margin-left: 8px"  id="comment_back_info"></span>
    </div>
</div>
{% else %}
<p><a href="{% url 'login' %}">登录</a>后才能评论哦</p>
{% endif %}
{#    评论区end#}



<script>
    {#  全局变量 #}
    let parentId = null;

     $('#btn_comment_submit').click(function () {
         let commentBody = $('#comment_body');
         let content = commentBody.val();		          // 获取评论内容
         if (parentId){						  // 截掉子评论的前缀
             let splitIndex = content.indexOf('\n') + 1;
             content = content.slice(splitIndex);
         }
         $.ajax({
             url: '{% url "article_comment" %}',
             type: 'post',
             data: {
                 'article_id': '{{ article_id }}',
                 'content': content,
                 'parent_id': parentId,			// 根评论时为null, 子评论时有值
             },
             success: function (args) {
                 if (args.code === 1000){
                     $('#comment_back_info').text(args.msg);
                     commentBody.val('');
                     let new_comment = `<li class="list-group-item">
                                                <span class="glyphicon glyphicon-comment">{{ request.user }}</span>
                                                <p class="small text-success">&nbsp;&nbsp;&nbsp;&nbsp;${content}</p>
                                        </li>`;
                     $('#comment_list').append(new_comment);  // 通过js的模版字符串语法临时渲染评论内容
                     parentId = null;                         // 重置, 避免子评论后无法根评论
                 }else{
                     $('#comment_back_info').text(args.msg);
                 }
             }
         })
     });
     {#点击回复按钮事件#}
     $('.btn_reply').click(function () {
         let replayToUser = $(this).attr('replyto');      //获取回复对象
         parentId = $(this).attr('parentid');     	  //获取回复评论的id
         let reply_msg = '@' + replayToUser + '\n';       //构造提示信息
         $('#comment_body').val(reply_msg).focus();

     })
</script>

后端

@login_required
def article_comment(request):
    if request.is_ajax():
        back_info = {'code': 1000}
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')

        if not content:
            back_info['code'] = 2000
            back_info['msg'] = '评论内容不能为空'
            return JsonResponse(back_info)
        with transaction.atomic():
            models.Article.objects.filter(pk=article_id).update(comment_counts=F('comment_counts')+1)
            models.Comment.objects.create(user=request.user, article_id=article_id, content=content, parent_id=parent_id)
            back_info['msg'] = '评论成功'
            return JsonResponse(back_info)
    else:
        return render(request, 'error404.html')
posted @ 2020-06-12 23:14  the3times  阅读(195)  评论(1编辑  收藏  举报