• django框架之BBS项目之评论功能


    内容回顾
        1. BBS项目 CMS
            1. 登录
                1. form组件
                2. auth模块
                3. 验证码
            2. 注册
                1. form组件
                    1. 生成html代码
                        直接for循环form_obj,就能够遍历所有字段
                    2. 验证
                        1. 默认的那些验证
                        2. 正则的验证
                        3. 全局钩子做确认密码的验证
                        4. 判断用户名是否已经存在
                            1. input框失去焦点就发ajax到后端判断
                            2. form组件中使用局部钩子来判断    
                        
                2. auth模块 --> 扩展auth_user表 --> create_user()
                    1. UserInfo这个类里面,avatar是一个FileField
                        avatar = models.FileField(upload_to="avatars/", default="avatars/default.png")
                    2. 注意事项:
                        1. FileField保存的是一个路径,而不是一个文件
                        2. upload_to:具体保存的文件路径就会在media目录下
                3. 上传头像
                    1. ajax如何上传文件
                    2. Django中media的配置
                        1. settings.py中:
                            - MEDIA_URL:别名
                            - MEDIA_ROOT:给用户上传的所有文件指定一个存放目录
                        2. urls.py中:
                            from django.views.static import serve
                            from django.conf import settings
                            
                            url(r'^media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT})
            3. 博客首页
                1. 文章列表(排样式)
                2. 分页
                    1. 封装的要彻底
                    2. 封装后的结果要有普适性(url要写成配置项)
            4. 个人博客主页
                1. 分类
                    1. 文章分类
                    2. 文章标签
                2. 日期归档
                    1. 日期格式化函数    --> MySQL内置的函数都有哪一些?      --> 《漫画数据库》
                        1. MySQL:DATE_FORMAT('字段', '格式')
                        2. sqlite:strftime('格式', '字段')
                    
                    2. ORM中如何执行原生的SQL语句
                        1. 使用extra()在执行ORM查询的同时,嵌入一些SQL语句
                        2. 直接执行原生SQL语句
                            from django.db import connection
                            cursor = connection.cursor()
                            cursor.execute('select id from userinfo;')
                            cursor.fetchall()    
                    3. 分组聚合
                        QuertySet.annotate()  --> 分组,前面查的是什么字段就按什么字段分组        
                        QuertySet.aggregate() --> 聚合,给QuerySet中的每个对象多一个属性
                        
                    4. 4合1路由
                        不同的路由可以使用同一个视图函数!!! --> 视图函数中通过参数的不同,实现不同的功能
            5. 文章详情页
                1. 母板继承
                2. inclusion_tag
                    1. 返回一段HTML代码,用数据填充的HTML代码
                    2. 具体的写法
                        1. 在app下面创建一个名为 templatetags 的 Python Package
                        2. 在上面的包中创建一个 py文件
                        3. 按照inclusion_tag的格式要求写功能函数
                            
                            from django import template
                            register = template.Library()
                            
                            @register.inclusion_tag(file='xx.html')
                            def show_menu(*arg):
                                ...
                                return {"k1": "v1"}
                                (xx.html中使用k1这个变量)    
                3. 点赞
                    1. ajax发送点赞的请求
                        1. 点赞必须是登录用户,没登录跳转到登录页面
                        2. 不能给自己的文章点赞
                        3. 一个用户只能给一篇文章点一次赞或踩一次
                    
                    2. 后端创建点赞记录(事务操作)
                        1. 创建新的点赞记录
                        2. 去对应的文章表里把点赞数更新一个
                    3. ORM事务操作
                        from django.db import transaction
                        
                        with transaction.atomic():
                            sql1;
                            sql2;
                            
                    4. Django模板语言里面的JS代码
                        如何在js中引用模板语言的变量,注意加引号!!!
                       

    接下来就开始写评论区的功能了,首先就是在页面布局:

    我们希望吧页面布局成这个样式:

    在bootstrap找到样式之后,作相应的改动:我们先想一下,在这个区域需要哪些数据,首先就是评论发表的时间,评论的作者,还有评论的内容。所以在文章详情页面的试图函数中应该传一些comment的参数。之后再页面中,通过for循环,逐个展示评论:

    <!--评论展示区 开始-->
        <h4>评论</h4>
        <div class="list-group comment-list">
            {% for comment in comment_list %}
                <div href="#" class="list-group-item">
                    <h4 class="list-group-item-heading comment-header">
                        <span>#{{ forloop.counter }}楼</span>
                        <span>{{ comment.create_time|date:'Y-m-d H:i' }}</span>
                        <span>{{ comment.user.username }}</span>
                        <span></span>
                    </h4>
                    <p class="list-group-item-text">{{ comment.content }}</p>
                </div>
            {% endfor %}
        </div>
        <!--评论展示区 结束-->

    接下来就是发表评论的区域了:

    同样是先布局,然后html代码:

    <!--发表评论区 开始-->
        <h4>发表评论</h4>
        <div>昵称:
            <input type="text"value="{{ request.user.username }}" disabled>
        </div>
        <div>
            <p>评论内容:</p>
            <textarea cols="60" rows="10"></textarea>
        </div>
        <div>
            <button class="btn btn-success">提交</button>
        </div>
        <!--发表评论区 结束-->

    接下来就是发送ajax请求了。

    我们能够很容易的写出来这步:

     //给评论按钮绑定点击事件
        $('#submit-comment').click(function () {
            var userid = '{{ request.user.id }}';
            var articleid = '{{ article_obj.id }}';
            var content = $('#new-comment').val();
            var csrf_token = $('[name="csrfmiddlewaretoken"]').val();
            $.ajax({
                url:'/comment/',
                type:'post',
                data:{
                    user_id:userid,
                    article_id:articleid,
                    content:content,
                    csrfmiddlewaretoken:csrf_token,
                },
                success:function (res) {
                    console.log(res)
                }
            })
        })
    试图函数也可以这样写;

    def
    comment(request): if request.method == 'POST': res = {'code':0} user_id = request.POST.get('user_id') article_id = request.POST.get('article_id') content = request.POST.get('content')
        
    with transaction.atomic():
         # 1.先去创建新评论
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
         # 2.再去更新该文章的评论数
    models.Article.objects.update(comment_count=F('comment_count')+1)
       return JsonResponse(res)

    这样基本的评论功能已经写出来了,给文章评论之后,刷新之后在页面上就能够展示出评论内容,

    但是我们实际的评论不是这样的,当我们评论之后,评论内容就会马上出现在展示区,不用通过刷新。这一步怎么实现呢?

    这就要求我们在发送请求成功之后的success函数中将它通过js操作展示出来,说白了就是在原来评论展示区的下面再添加一个展示区。所以试图函数要返回一些数据:

    views.py代码:

    def comment(request):
        if request.method == 'POST':
            res = {'code':0}
            user_id = request.POST.get('user_id')
            article_id = request.POST.get('article_id')
            content = request.POST.get('content')     
        
    with transaction.atomic():
         # 1.先去创建新评论
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
         # 2.再去更新该文章的评论数
    models.Article.objects.update(comment_count=F('comment_count')+1)
        res['data'] = { 
          'id':comment_obj.id,
          'username':comment_obj.user.username,
          'content':comment_obj.content,
          'create_time':comment_obj.create_time.strftime("Y-m-d") # 这部如果看不懂的话就去复习一下那三种时间格式的相互转化 }

        return JsonResponse(res)

    在说js操作代码之前,我们先来复习一个知识:就是js中的字符串格式化输出。

     # 给评论按钮绑定点击事件
    $('#submit-comment').click(function () { var userid = '{{ request.user.id }}'; var articleid = '{{ article_obj.id }}'; var content = $('#new-comment').val(); var csrf_token = $('[name="csrfmiddlewaretoken"]').val(); $.ajax({ url:'/comment/', type:'post', data:{ user_id:userid, article_id:articleid, content:content, csrfmiddlewaretoken:csrf_token, }, success:function (res) { console.log(res) if(res.code===0){ var data = res.data //先计算原来.comment-list的后代有几个div,即有几个人评论,然后再加一 var num = $('.comment-list>div').length + 1; //创建评论成功后,通过js在评论区列表在添加一条评论 var commenthtml = ` <div href="#" class="list-group-item"> <h4 class="list-group-item-heading comment-header"> <span>#${ num }楼</span> <span>${ data.create_time }</span> <span>${ data.username }</span> <span></span> </h4> <p class="list-group-item-text">${ data.content }</p> </div> ` //在原来的评论列表后面添加一条 $('.comment-list').append(commenthtml) //情空 textarea $('#new-comment').val('') } } }) })

    上面写的是添加父评论。

    接下来添加子评论,就是回复的部分了:

    博客园中当我们点击回复按钮时,评论区会显示这样:

    分为两部:当我们点击回复标签时,光标就会聚焦在评论区,并且出现了@xxx

    这个如何实现呢?

    $('.replay').click(function () {
            //光标聚焦在textarea
            //出现@xxx
            var replayname = $(this).prev().text() //取到当前标签的前一个标签的文本
            $('#new-comment').focus().val('@'+replayname+'
    ')
        })

    comment表里有parent_comment字段,我们应该怎么取到这个字段的id呢?

    当我们点击提交的时候,我们不管有没有父评论都给后端传一个parent_id,我们给评论的那个div加一个自定义的id    

    <div href="#" class="list-group-item" my_id="{{ comment.id }}">
    <!--评论展示区 开始-->
        <h4>评论</h4>
        <div class="list-group comment-list">
            {% for comment in comment_list %}
                <div href="#" class="list-group-item" my_id="{{ comment.id }}">
                    <h4 class="list-group-item-heading comment-header">
                        <span>#{{ forloop.counter }}楼</span>
                        <span>{{ comment.create_time|date:'Y-m-d H:i' }}</span>
                        <span>{{ comment.user.username }}</span>
                        <span></span>
                    </h4>
              {% if comment.parent_comment %}
                            <span style="display: block">@{{ comment.parent_comment.user.username }}</span>
                            <p class="list-group-item-text well">
                                {{ comment.parent_comment.content }}
                            </p>
                      {% endif %} <p class="list-group-item-text">{{ comment.content }}</p> </div> {% endfor %} </div> <!--评论展示区 结束-->

    那么我们如何区分谁是父评论谁是子评论呢?当我们点击回复的时候,产生的评论就是子评论,直接点击提交就是父评论,那我们应该怎么操作呢?

    我们把这个自定义的id添加到提交的按钮上,当我们点击提交的按钮是进行判断,如果这个id存在,就说明是子评论,否则就是父评论。

      //点击回复按钮时的绑定事件
    $('.replay').click(function () { //光标聚焦在textarea //出现@xxx var replayname = $(this).prev().text() ;//取到当前标签的前一个标签的文本 $('#new-comment').focus().val('@'+replayname+' '); //把当前评论的id值,存到提交的按钮中 var pID = $(this).parent().parent().attr('my-id'); $('#submit-comment').data('pid',pID) })
     //给评论按钮绑定点击事件
        $('#submit-comment').click(function () {
            var userid = '{{ request.user.id }}';
            var articleid = '{{ article_obj.id }}';
            var content = $('#new-comment').val();
            var csrf_token = $('[name="csrfmiddlewaretoken"]').val();
            var parentId = $(this).data('pid') || '';
            if(parentId){
            //因为在添加自评论时,会出现@xxx的东西所以我们按照索引把他去除 content
    = content.slice(content.indexOf(' ')+1,); } $.ajax({ url:'/comment/', type:'post', data:{ parent_id:parentId, user_id:userid, article_id:articleid, content:content, csrfmiddlewaretoken:csrf_token, }, success:function (res) { console.log(res) if(res.code===0){ var data = res.data //先计算原来.comment-list的后代有几个div,即有几个人评论,然后再加一 var num = $('.comment-list>div').length + 1; //创建评论成功后,通过js在评论区列表在添加一条评论 var commenthtml = ` <div href="#" class="list-group-item"> <h4 class="list-group-item-heading comment-header"> <span>#${ num }楼</span> <span>${ data.create_time }</span> <span>${ data.username }</span> <span></span> </h4> <p class="list-group-item-text">${ data.content }</p> </div> `; //在原来的评论列表后面添加一条 $('.comment-list').append(commenthtml); //情空 textarea $('#new-comment').val('');
                //当我们点击提交按钮时,应该把自定义的id去掉,不然下一次点击提交的时候,还是有这个id值 $(
    "#submit-comment").removeData("pid"); } } }) });

    我们后端也能拿到parent_id。

    def comment(request):
        if request.method == 'POST':
            res = {'code':0}
            user_id = request.POST.get('user_id')
            article_id = request.POST.get('article_id')
            content = request.POST.get('content')
            parent_id = request.POST.get("parent_id")
            #创建评论
            with transaction.atomic():
                # 1.先去创建新评论
                if parent_id:
                    #添加自评论
                    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=parent_id)
                else:
                    #添加父评论
                    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
                # 2.再去更新该文章的评论数
                models.Article.objects.update(comment_count=F('comment_count')+1)
            res['data'] = {
                'parent_id':parent_id,
                'id':comment_obj.id,
                'username':comment_obj.user.username,
                'content':comment_obj.content,
                'create_time':comment_obj.create_time.strftime("Y-m-d")
            }
            return JsonResponse(res)

     这样添加的评论还不够完美,就是添加的评论没有回复这个功能,  

     //给评论按钮绑定点击事件
        $('#submit-comment').click(function () {
            var userid = '{{ request.user.id }}';
            var articleid = '{{ article_obj.id }}';
            var content = $('#new-comment').val();
            var csrf_token = $('[name="csrfmiddlewaretoken"]').val();
            var parentId = $(this).data('pid') || '';
            if(parentId){
                //因为在添加自评论时,会出现@xxx的东西所以我们按照索引把他去除
                content = content.slice(content.indexOf('
    ')+1,);
            }
            $.ajax({
                url:'/comment/',
                type:'post',
                data:{
                    parent_id:parentId,
                    user_id:userid,
                    article_id:articleid,
                    content:content,
                    csrfmiddlewaretoken:csrf_token,
                },
                success:function (res) {
                    console.log(res)
                    if(res.code===0){
                        var data = res.data
                        //先计算原来.comment-list的后代有几个div,即有几个人评论,然后再加一
                        var num = $('.comment-list>div').length + 1;
                        //创建评论成功后,通过js在评论区列表在添加一条评论
                        var commenthtml = `
                        <div href="#" class="list-group-item">
                    <h4 class="list-group-item-heading comment-header">
                        <span>#${ num }楼</span>
                        <span>${ data.create_time }</span>
                        <span>${ data.username }</span>
                        <span class="pull-right replay">回复</span>
                    </h4>
                        <p class="list-group-item-text">${ data.content }</p>
                    </div>
                        `;
                        //在原来的评论列表后面添加一条
                        $('.comment-list').append(commenthtml);
                        //情空 textarea
                        $('#new-comment').val('');
                        //当我们点击提交按钮时,应该把自定义的id去掉,不然下一次点击提交的时候,还是有这个id值
                        $("#submit-comment").removeData("pid");
                    }
                }
            })
        });

    添加回复这个按钮后,不能实现点击,这就用到了事件委托,当一个事件不能完成时,我们把它委托给他的父标签做将来的动作。

        //给回复按钮绑定事件
        //$('.replay').click(function () {
        //事件委托,
         $('.comment-list').on('click','.replay',function () {
            //光标聚焦在textarea
            //出现@xxx
            var replayname = $(this).prev().text() ;//取到当前标签的前一个标签的文本
            $('#new-comment').focus().val('@'+replayname+'
    ');
            //把当前评论的id值,存到提交的按钮中
            var pID = $(this).parent().parent().attr('my-id');
            $('#submit-comment').data('pid',pID)
        })

    这就是完整的代码了

    不懂得地方看视频,(看视频day79. 004)

    上面设计到的知识点,jquery中的data方法,就是我们可以给任意一个jQuery对象添加一个值。

    js中的三元运算:

     还有就是事件委托

  • 相关阅读:
    [转]How can I create a design netlist without including my source design files?
    [转]基于FPGA的以太网开发
    [转]GMII/RGMII/SGMII/TBI/RTBI接口信号及时序介绍
    [原]Altium画PCB时鼠标十字不能对准焊盘中心
    [转]Altera特殊管脚的使用(适用全系列Altera FPGA,MSEL区别除外)-来自altera论坛
    [转]STM32正交编码器驱动电机
    [转]使用D触发器制作正交编码器的鉴相电路
    [转]解决STM32开启定时器时立即进入一次中断程序问题
    [转]ISE iMPACT bit生成mcs
    [转]NiosII处理器软件代码优化方法
  • 原文地址:https://www.cnblogs.com/yb635238477/p/9508458.html
Copyright © 2020-2023  润新知