DAY14 新闻评论的实现

新闻评论

页面评论框显示

今天我们来实现新闻评论,评论只有登录用户可以进行,当用户没有登录时,是不能进行的。

第一步获取用户登录状态并进行评论框不同的展示

当用户未登录时评论框显示的是下面的内容,当用户点击时弹出登录框提示用户进行登录

 

 

 

 当用户登录时,这时用户就可以进行评论的操作:

 

 

这个展示判断的条件还是用户登录状态的判断,且页面在新闻详情页,我们之前就进行过用户状态获取的判断,并传入了前台页面的数据user 进行判断

这里直接改前端就好了。只是评论框的修改,评论的内容展示放在后面

前端对应模块的修改:

 

 

 

 完成之后的测试就用户登录和退出是否展示对应的评论框就可以了

评论数据后台代码

这是个新的接口所以我们按照接口设计来写新的视图,还是写在news模块的views里

,收藏的后面

接口设计图:

 

 

 首先还是建立对应的视图函数,指定请求方式为post,和文档注释模块功能的描述

 

 

 完成还后还是进行分析并在注释里分步骤写上我们思路。先简单写下思路吧。

首先评论是用户登录后才能进行,首先还是对用户登录状态进行判断,然后是接收参数

news_id: 新闻的id,用来维持评论和新闻的关系,也就是确认评论在哪条新闻中进行

comment : 评论,我们需要将用户输入的评论保存到对应的评论表中,并加入news_id的外键关联和用户ID的关联,评论是用户在对应的新闻完成的操作。

parent_id:父评论,存在评论表里的字段,只有当用户在已有的评论里进行评论时,那么存入表中的评论就会加上这个字段,也就是获取已有评论的id并将它加入到父评论字段里面来建立关系

前端获取到后来进行对应的展示。

数据的校验,是否传齐,数据类型是否正确,查询数据库对应的数据是否存在等

校验完成后往表中存入评论,分为有父评和没有父评的情况

最后返回结果,根据我们的结果来进行回调函数的使用

完整步骤图:

 

 

 

第一步

# 1. 获取用户登录状态
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录") 

 

通过装饰器来进行user的获取,也就是用户的登录状态和数据查询。并进行判断返回符合接口规范的json数据

第二步 

# 2. 接收参数 (news_id,comment,parent_id)
    news_id = request.json.get("news_id")
    comment = request.json.get("comment")
    parent_id = request.json.get("parent_id")

 传入的是json数据,安装对应的格式编写代码就可以了

第三步

# 3.校验参数
#     3.1 校验参数是否齐全
    if not all([news_id,comment]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数缺失")
#     3.2 校验 news_id ,parent_id数据类型
    try:
        news_id = int(news_id)
        if parent_id:
            parent_id = int(parent_id)
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR,errmsg="参数错误")
#     3.3 校验新闻是否存在
    try:
        news = News.query.filter_by(id = news_id).first()
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="数据读取失败")
    if not news :
        return jsonify(errno=RET.NODATA,errmsg="新闻不存在")
#     3.4 校验父评论是否存在                     
    if parent_id :             #只有当传了parent_id,也就是有值时进行判断
        try:
            parent_comment = Comment.query.filter_by(id = parent_id).first()
        except Exception as e :
                current_app.logger.error(e)
                return jsonify(errno=RET.DBERR, errmsg="数据读取失败")
        if not parent_comment :           #对查询结果进行判断
            return jsonify(errno=RET.NODATA,errmsg="评论未找到") 

 

首先判断接口中必须要传的参数是否齐全,然后进行数据类型的校验,通过在异常捕捉try里面强转来进行,并对异常作出处理

将需要查询数据库的查询操作放在最后,首先进行查询,最后再对查询的结果进行判断,不要忘记对结果进行判断,第三方数据库查询必须进行

异常捕捉。

第四步

# 4. 对评论进行操作
    new_comment = Comment()        #实例化新的评论对象
    new_comment.user_id = user.id   #添加评论用户     
    new_comment.news_id = news.id   #添加对应的新闻
    new_comment.content = comment   #添加评论内容
    if parent_id :                 #父评只有当有值时进行
        new_comment.parent_id = parent_id
    try:
        db.session.add(new_comment)
        db.session.commit()
    except Exception as e :
            current_app.logger.error(e) 
            db.session.rollback()           #事物回滚
            return jsonify(errno=RET.DBERR,errmsg="评论失败") 

 

我们需要先从评论类里实例化新的评论对象,然后进行数据的添加,这里建议将评论类里的所有字段复制过来,一一查看并作出添加,就不会出现有字段遗忘没有添加的情况

这里我我们需要将发表评论的用户和对应的新闻ID进行添加,还有评论的内容,父评只有当有值时进行,操作数据库进行异常捕捉,添加数据进行事物回滚

第五步

# 5.返回数据
    return jsonify(errno=RET.OK,errmsg="成功",data = new_comment.to_dict()) #返回评论的字典数据

 

这里除了返回json数据外我们还需要将评论的字典数据进行返回,让前端进行相关内容的评价展示(后面会补上前端代码),建议以后都创建一个将对象属性转换为字典数据的函数

对应的函数代码格式:

 

 

 

前端代码

前端js代码

没有父评

 // 评论提交
    $(".comment_form").submit(function (e) {
        e.preventDefault();
        var news_id = $(this).attr('data-newsid')
        var news_comment = $(".comment_input").val();

        if (!news_comment) {
            alert('请输入评论内容');
            return
        }
        var params = {
            "news_id": news_id,      
            "comment": news_comment   #没有parent_id
        };
        $.ajax({
            url: "/news/news_comment",
            type: "post",
            contentType: "application/json",
            headers: {
                "X-CSRFToken": getCookie("csrf_token")
            },
            data: JSON.stringify(params),
            success: function (resp) {
                if (resp.errno == '0') {
                    var comment = resp.data       #传的字典数据
                    // 拼接内容
                    var comment_html = ''
                    comment_html += '<div class="comment_list">'
                    comment_html += '<div class="person_pic fl">'
                    if (comment.user.avatar_url) {
                        comment_html += '<img src="' + comment.user.avatar_url + '" alt="用户图标">'
                    }else {
                        comment_html += '<img src="../../static/news/images/person01.png" alt="用户图标">'
                    }
                    comment_html += '</div>'
                    comment_html += '<div class="user_name fl">' + comment.user.nick_name + '</div>'
                    comment_html += '<div class="comment_text fl">'
                    comment_html += comment.content
                    comment_html += '</div>'
                    comment_html += '<div class="comment_time fl">' + comment.create_time + '</div>'

                    comment_html += '<a href="javascript:;" class="comment_up fr" data-commentid="' + comment.id + '" data-newsid="' + comment.news_id + '">赞</a>'
                    comment_html += '<a href="javascript:;" class="comment_reply fr">回复</a>'
                    comment_html += '<form class="reply_form fl" data-commentid="' + comment.id + '" data-newsid="' + news_id + '">'
                    comment_html += '<textarea class="reply_input"></textarea>'
                    comment_html += '<input type="button" value="回复" class="reply_sub fr">'
                    comment_html += '<input type="reset" name="" value="取消" class="reply_cancel fr">'
                    comment_html += '</form>'

                    comment_html += '</div>'
                    // 拼接到内容的前面
                    $(".comment_list_con").prepend(comment_html)
                    // 让comment_sub 失去焦点
                    $('.comment_sub').blur();
                    // 清空输入框内容
                    $(".comment_input").val("")


                    // 更新评论条数
                    updateCommentCount()
                }else {
                    alert(resp.errmsg)
                }
            }
        })
    });

    $('.comment_list_con').delegate('a,input','click',function(){

        var sHandler = $(this).prop('class');

        if(sHandler.indexOf('comment_reply')>=0)
        {
            $(this).next().toggle();
        }

        if(sHandler.indexOf('reply_cancel')>=0)
        {
            $(this).parent().toggle();
        }

 有父评(在已有的评论里进行评论)

        // TODO : 回复评论
        if(sHandler.indexOf('reply_sub')>=0)
        {
            var $this = $(this)
            var news_id = $this.parent().attr('data-newsid')
            var parent_id = $this.parent().attr('data-commentid')
            var comment = $this.prev().val()

            if (!comment) {
                alert('请输入评论内容')
                return
            }
            var params = {
                "news_id": news_id,
                "comment": comment,
                "parent_id": parent_id    #有parent_id
            }
            $.ajax({
                url: "/news/news_comment",
                type: "post",
                contentType: "application/json",
                headers: {
                    "X-CSRFToken": getCookie("csrf_token")
                },
                data: JSON.stringify(params),
                success: function (resp) {
                    if (resp.errno == "0") {
                        var comment = resp.data    #我们传入的字典数据
                        // 拼接内容
                        var comment_html = ""
                        comment_html += '<div class="comment_list">'
                        comment_html += '<div class="person_pic fl">'
                        if (comment.user.avatar_url) {
                            comment_html += '<img src="' + comment.user.avatar_url + '" alt="用户图标">'
                        }else {
                            comment_html += '<img src="../../static/news/images/person01.png" alt="用户图标">'
                        }
                        comment_html += '</div>'
                        comment_html += '<div class="user_name fl">' + comment.user.nick_name + '</div>'
                        comment_html += '<div class="comment_text fl">'
                        comment_html += comment.content
                        comment_html += '</div>'
                        comment_html += '<div class="reply_text_con fl">'
                        comment_html += '<div class="user_name2">' + comment.parent.user.nick_name + '</div>'
                        comment_html += '<div class="reply_text">'
                        comment_html += comment.parent.content
                        comment_html += '</div>'
                        comment_html += '</div>'
                        comment_html += '<div class="comment_time fl">' + comment.create_time + '</div>'

                        comment_html += '<a href="javascript:;" class="comment_up fr" data-commentid="' + comment.id + '" data-newsid="' + comment.news_id + '">赞</a>'
                        comment_html += '<a href="javascript:;" class="comment_reply fr">回复</a>'
                        comment_html += '<form class="reply_form fl" data-commentid="' + comment.id + '" data-newsid="' + news_id + '">'
                        comment_html += '<textarea class="reply_input"></textarea>'
                        comment_html += '<input type="button" value="回复" class="reply_sub fr">'
                        comment_html += '<input type="reset" name="" value="取消" class="reply_cancel fr">'
                        comment_html += '</form>'

                        comment_html += '</div>'
                        $(".comment_list_con").prepend(comment_html)
                        // 请空输入框
                        $this.prev().val('')
                        // 关闭
                        $this.parent().hide()

                        // 更新评论条数
                        updateCommentCount()
                    }else {
                        alert(resp.errmsg)
                    }
                }
            })
        }
    })

 

 

页面评论展示

后台注释和代码:

 

 

 

#5.评论相关展示
    comments = []
    try:
        comments = Comment.query.filter_by(news_id=news.id).order_by(Comment.create_time.desc()).all()
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="评论读取异常")
    comments = [comment.to_dict() for comment in comments]    #将对象进行字典数据的转换
# 生成保存用户变量的上下文并传到前台进行相关判断处理
    context = {
        "user": user,
        "news_click_list": news_click_list,
        "news":news.to_dict(),
        "is_collected":is_collected,
        "comments" : comments
    } 

 

这里就讲一下查询,其他的和之前的大致是一样的

我们需要通过新闻的id 来进行查询,查询评论中所有新闻id一样的评论来进行展示,并且展示的内容是通过时间的顺序来进行的。所有的评论执行器是all(),最新的在最上面也就是降序(desc()).

 

前端HTML文件: 

 

     <div class="comment_count">
            {{ news.comments_count }}条评论      #新闻的关联查询字段,来获取评论数 
        </div>
        

            <div class="comment_list_con">
            {% for comment in comments %}
                <div class="comment_list">
                    <div class="person_pic fl">
                        <img src="{% if comment.user.avatar_url %}
                            {{ comment.user.avatar_url }}
                        {% else %}
                            ../../static/news/images/person01.png
                        {% endif %}" alt="用户图标">
                    </div>
                    <div class="user_name fl">{{ comment.user.nick_name }}</div>
                    <div class="comment_text fl">{{ comment.content }}</div>
                    {% if comment.parent %}
                        <div class="reply_text_con fl">
                            <div class="user_name2">{{ comment.parent.user.nick_name }}</div>
                            <div class="reply_text">
                                {{ comment.parent.content }}
                            </div>
                        </div>
                    {% endif %}
                    <div class="comment_time fl">{{ comment.create_time }}</div>

{#                    <a href="javascript:;" class="comment_up {% if comment.is_like %}has_comment_up{% endif %} fr" data-commentid="{{ comment.id }}" data-newsid="{{ comment.news_id }}">赞</a>#}

                    <a href="javascript:;" class="comment_up
                    {% if comment.is_like %}
                        has_comment_up
                    {% endif %} fr"
                       data-commentid="{{ comment.id }}"
                       data-likecount="{{ comment.like_count }}"
                       data-newsid="{{ news.id }}">
                    {% if comment.like_count > 0 %}
                        {{ comment.like_count }}
                    {% else %}
                        赞
                    {% endif %}
                    </a>

                    <a href="javascript:;" class="comment_reply fr">回复</a>
                    <form class="reply_form fl" data-commentid="{{ comment.id }}" data-newsid="{{ news.id }}">
                        <textarea class="reply_input"></textarea>
                        <input type="button" value="回复" class="reply_sub fr">
                        <input type="reset" name="" value="取消" class="reply_cancel fr">
                    </form>
                </div>
            {% endfor %}
        </div>

 

新闻类关联字段,新闻查询时可以查到所有的评论,数量在转换字典中进行查询展示

字段

 

数量(.count())

 

 

 

 

 

 

后台完整代码:

@news_blue.route("/news_comment",methods = ["POST"])
@user_login_data
def news_comment():
    """
    新闻评论
    1. 获取用户登录状态
    2. 接收参数 (news_id,comment,parent_id)
    3.校验参数
        3.1 校验参数是否齐全
        3.2 校验 news_id ,parent_id)数据类型
        3.3 校验新闻和父评论是否存在
        3.4 校验父评论是否存在
    4. 创建评论数据
    5.返回数据
    :return: 
    """
# 1. 获取用户登录状态
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")
# 2. 接收参数 (news_id,comment,parent_id)
    news_id = request.json.get("news_id")
    comment = request.json.get("comment")
    parent_id = request.json.get("parent_id")
# 3.校验参数
#     3.1 校验参数是否齐全
    if not all([news_id,comment]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数缺失")
#     3.2 校验 news_id ,parent_id数据类型
    try:
        news_id = int(news_id)
        if parent_id:
            parent_id = int(parent_id)
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR,errmsg="参数错误")
#     3.3 校验新闻是否存在
    try:
        news = News.query.filter_by(id = news_id).first()
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="数据读取失败")
    if not news :
        return jsonify(errno=RET.NODATA,errmsg="新闻不存在")
#     3.4 校验父评论是否存在
    if parent_id :
        try:
            parent_comment = Comment.query.filter_by(id = parent_id).first()
        except Exception as e :
                current_app.logger.error(e)
                return jsonify(errno=RET.DBERR, errmsg="数据读取失败")
        if not parent_comment :
            return jsonify(errno=RET.NODATA,errmsg="评论未找到")
# 4. 对评论进行操作
    new_comment = Comment()
    new_comment.user_id = user.id
    new_comment.news_id = news.id
    new_comment.content = comment
    if parent_id :
        new_comment.parent_id = parent_id
    try:
        db.session.add(new_comment)
        db.session.commit()
    except Exception as e :
            current_app.logger.error(e)
            db.session.rollback()
            return jsonify(errno=RET.DBERR,errmsg="评论失败")
# 5.返回数据
    return jsonify(errno=RET.OK,errmsg="成功",data = new_comment.to_dict())

  

posted @ 2021-10-22 20:53  和风的夏天  阅读(118)  评论(0)    收藏  举报