Django 评论楼创建

Django 评论楼创建

【零】最终效果预览

在这里插入图片描述

在这里插入图片描述

【一】介绍

(1)情况说明

  • 在Django模型层中有这么个字段
parent = models.ForeignKey(to='self', on_delete=models.CASCADE, verbose_name="父评论ID", null=True, blank=True)
  • 这个字段是一对多的外键字段
    • 其中to='self'说明是自关联
    • 当有子评论时,通过添加父评论的ID实现主评论和子评论的相关联
  • 那么就可以得到这么一个关系图

在这里插入图片描述

  • 这个是我们理想中应该存在的情况
    • 每一个评论都可以有子评论,且可以有多个子评论
    • 这就是数据结构中的森林,每一个评论楼都是一个N叉树
    • 每一个子评论都可以根据父评论ID获取父评论的所有信息
      • 包括父评论用户名等所有信息
  • 所以就存在这么一个问题
    • 如何将每一个子评论渲染到对应的评论楼中

(2)解决办法思路

  • 既然这是个森林那么就可以使用广度优先算法解决

    • 难度相对来说有点复杂
  • 有没有简单的办法呢

    • 只要是个树那么就很难不用到广度优先遍历
    • 所以就是破坏这个树
  • 这个是新的评论关系

    • 模型层部分代码

    • # 自动关联主评论,这个只是主评论楼ID
      parent = models.ForeignKey(to='self', on_delete=models.CASCADE, verbose_name="主评论楼ID", null=True, blank=True, related_name='children')
      # 将主评论ID和用户名分开保存
      parent_name = models.CharField(max_length=32, verbose_name='父评论名字', null=True, blank=True)
      
  • 这是新的评论楼关系图
    在这里插入图片描述

  • 说明

    • 每一个评论下都渲染这个评论下的所有子评论
    • 现在任然存在子评论的子评论
      • 不过子评论的id直接指向评论楼楼主的ID
    • 但是这样就少了被回复的那个人的用户名
      • 父评论ID智能找到楼主信息
    • 所以还有一个字段
      • 保存被回复的用户名称

【二】代码说明

(1)模型层代码说明

  • 主要说明一下parent字段的related_name

    • 这个属性定义了反向关系

    • 通过反向关系可以拿到所有的子评论

    • 但是还需要定义一个获得子评论的方法

    • def get_children(self):
          return self.children.all()
      
    • 这样就可以很轻松的拿到所有评论楼下的所有子评论信息

class Comment(BaseModel):
    content = models.CharField(max_length=255, verbose_name="评论内容")
    is_deleted = models.BooleanField(default=False, verbose_name="评论是否哦被删除")

    # 自动关联主评论,这个只是主评论楼ID
    parent = models.ForeignKey(to='self', on_delete=models.CASCADE, verbose_name="主评论楼ID", null=True, blank=True,
                           related_name='children')
    # 将主评论ID和用户名分开保存
    parent_name = models.CharField(max_length=32, verbose_name='父评论名字', null=True, blank=True)
    
    # 关联用户
    user = models.ForeignKey(to='user.UserInfo', on_delete=models.CASCADE, verbose_name="用户")
    # 关联文章
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, verbose_name='文章')

    class Meta:
        db_table = 'comment'
        verbose_name_plural = '评论表'

    def get_children(self):
        return self.children.all()

(2)视图层代码说明

(2.1)展示评论楼说明
  • 这个很简单
    • 首先对过滤掉非本文章的评论
    • 然后遍历出所有评论楼
      • 就是父评论为空的评论
def article_detail(request, username, article_id):
    user_obj = UserInfo.objects.filter(username=username)
    if not user_obj:
        return render(request, 'error.html', locals())

    # 文章详情
    article_info_obj = Article.objects.filter(pk=article_id).first()

    # 文章评论
    all_comment_queryset = Comment.objects.filter(article=article_info_obj)
    comment_queryset = all_comment_queryset.filter(parent=None)

    return render(request, 'article_detail.html', locals())

(2.2)添加评论说明
  • 首先获取前端发送的所有信息,主要的是
    • 评论楼ID和父评论名字
    • 如果这个有值,那么就是子评论
    • 如果这个没有值,那么就是一个新的评论楼
  • 这里的创建新评论楼的好处
    • 不需要判断评论楼ID和父评论是否有值
    • 因为模型层是允许为空的
@login_required
def comment(request):
    if request.is_ajax():
        # 获取前端信息
        article_id = request.POST.get("article_id")
        parent_id = request.POST.get("parentId")
        content = request.POST.get("content")
        username = request.POST.get("username")
        # 评论不能为空判断
        if not all([article_id, content]):
            return json_response(code=2002, error='评论不能为空')
        # 创建评论
        Comment.objects.create(content=content, parent_id=parent_id, user=request.user, article_id=article_id,
                               parent_name=username)
        # 评论数增加
        article_obj = Article.objects.filter(pk=article_id).first()
        article_obj.comment_num += 1
        article_obj.save()
        return json_response()
    return json_response(code=2001, error='非ajax请求')

(3)模板层代码说明

(3.1)展示评论
  • 首先遍历所有的评论楼
    • 然后通过模型层的方法get_children()
    • 获得所有的子评论
      • 再次循环遍历渲染子评论信息即可
  • 需要注意的点是
    • 所有的子评论都要保存评论楼的楼主ID
    • 这样才是新的模型层关系
<li class="media" style="border: 1px solid #72CDFC; border-top-width: 2px; border-bottom: none;"
id="comment-media-list">
{% for comment_obj in comment_queryset %}
    {#评论头部#}
    <div class="media-heading" style="margin-bottom: 10px">
        <span>#{{ forloop.counter }}楼</span>
        <span>{{ comment_obj.create_time|date:"Y-m-d H:i" }}</span>
        <span>{{ comment_obj.user.username }}</span>
        <span><a href="javascript:;" class="pull-right comment-replay"
                 username="{{ comment_obj.user.username }}"
                 comment-id="{{ comment_obj.pk }}">回复@{{ comment_obj.user.username }}</a></span>
    </div>
    {#评论内容#}
    <div class="media-body">
        {{ comment_obj.content }}
        {% if comment_obj.get_children %}
            <a class="pull-right" role="button" data-toggle="collapse"
               href="#collapseExample{{ comment_obj.pk }}"
               aria-expanded="false" aria-controls="collapseExample">
                查看更多
            </a>
            <div class="collapse" id="collapseExample{{ comment_obj.pk }}">
                <div class="well">
                    {% for children in comment_obj.get_children %}
                        <p style="margin-bottom: 5px">
                            {{ children.user.username }}@{{ children.parent_name }}
                            <span><a href="javascript:;" class="pull-right comment-replay"
                                     username="{{ children.user.username }}"
                                     comment-id="{{ comment_obj.pk }}">回复@{{ children.user.username }}</a></span>
                        </p>
                        <div>
                            {{ children.content }}
                        </div>
                        <hr style="border: 1px solid #72CDFC; width: 100%; margin-bottom: 0;">
                    {% endfor %}
                </div>
            </div>
        {% endif %}
    </div>
    <hr style="border: 1px solid #72CDFC; width: 100%; margin-bottom: 0;">
{% endfor %}
</li>
(3.2)添加评论Ajax
  • 回复评论的处理
    • 获取被回复对象的用户名和评论楼的ID
    • 添加指定格式
    • 渲染到评论框中,并聚焦focus()
  • 提交评论给后端Ajax
    • 添加到评论框中的内容并不是全都要保存在数据库中比如@admin
    • 所以先对这部分进行切分处理
    • 然后传递信息给后端对应的路由
  • 从后端拿到返回信息
    • 错误的信息,进行指定位置的渲染
    • 正确的信息,先显示在底部,提供一个刷新按钮
      • 点击刷新即可完成评论的添加
<script>
    $(document).ready(function () {
        // 父评论默认是空的
        let parentId = null
        // 回复平理论处理
        $(".comment-replay").click(function () {
            let commentUsername = $(this).attr('username')
            parentId = $(this).attr('comment-id')
            // 拼接字符到评论框,并且聚焦
            $("#comment-text").val("@" + commentUsername + '\n').focus()
        })

        $("#comment-submit").click(function (e) {
            e.preventDefault()
            // 获取评论类容
            let content = $("#comment-text").val()
            // 对次评论进行头部处理
            if (parentId) {
                let indexNum = content.indexOf('\n') + 1
                // 使用trim移除前后的空白名
                // 保留回复人的信息
                var username = content.slice(1, indexNum).trim();
                // 评论内瓤
                content = content.slice(indexNum).trim();
            }
            // 发送ajax请求
            $.ajax({
                url: "{% url 'comment' %}",
                type: 'post',
                data: {
                    "article_id": "{{ article_info_obj.pk }}",
                    "content": content,
                    "csrfmiddlewaretoken": "{{ csrf_token }}",
                    "parentId": parentId,
                    "username": username,
                },
                success: function (response) {
                    if (response.code === 2000) {
                        // 清空评论框中的内容
                        $("#comment-text").val('')
                        // 评论者的名字
                        let userName = '{{ request.user.username }}'
                        // 使用模板语法渲染信息
                        let newComment = `
                    <div class="media-heading" style="margin-bottom: 10px">
                        <span>${userName}</span>&nbsp;&nbsp;&nbsp;
                <span><a onclick="window.location.reload()" style="color: blue">刷新</a></span>
                    </div>
                    <div class="media-body">
                        ${content}
                    </div>
                    <hr style="border: 1px solid #72cdfc; width: 100%; margin-bottom: 0;">
`
                        // 添加到末尾
                        $("#comment-media-list").append(newComment)

                    } else {
                        $("#comment-error").text(response.error)
                    }
                    // 请求结束以后需要重置
                    parentId = null
                }
            })
        })
    })
</script>
  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值