Django 评论楼创建

Django 评论楼创建

【零】最终效果预览

【一】介绍

(1)情况说明

  • 在Django模型层中有这么个字段
python 复制代码
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

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

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

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

    python 复制代码
    def get_children(self):
        return self.children.all()
    • 这样就可以很轻松的拿到所有评论楼下的所有子评论信息
python 复制代码
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)展示评论楼说明
  • 这个很简单
    • 首先对过滤掉非本文章的评论
    • 然后遍历出所有评论楼
      • 就是父评论为空的评论
python 复制代码
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和父评论是否有值
    • 因为模型层是允许为空的
python 复制代码
@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
    • 这样才是新的模型层关系
html 复制代码
<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
    • 所以先对这部分进行切分处理
    • 然后传递信息给后端对应的路由
  • 从后端拿到返回信息
    • 错误的信息,进行指定位置的渲染
    • 正确的信息,先显示在底部,提供一个刷新按钮
      • 点击刷新即可完成评论的添加
python 复制代码
<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>
相关推荐
睡觉谁叫~~~38 分钟前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
丕羽3 小时前
【Pytorch】基本语法
人工智能·pytorch·python
2401_865854883 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
bryant_meng3 小时前
【python】Distribution
开发语言·python·分布函数·常用分布
AskHarries3 小时前
Spring Boot集成Access DB实现数据导入和解析
java·spring boot·后端
2401_857622663 小时前
SpringBoot健身房管理:敏捷与自动化
spring boot·后端·自动化
程序员阿龙3 小时前
基于SpringBoot的医疗陪护系统设计与实现(源码+定制+开发)
java·spring boot·后端·医疗陪护管理平台·患者护理服务平台·医疗信息管理系统·患者陪护服务平台
程思扬4 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
阿华的代码王国4 小时前
【Spring】——SpringBoot项目创建
java·spring boot·后端·启动类·target文件
m0_594526304 小时前
Python批量合并多个PDF
java·python·pdf