BBS项目文章详情页点赞点踩以及评论

一、编写思路概览

  • 每个功能思路其实大差不差 先开路由在写视图函数返回个前端页面
  • 文章详情 点赞点踩 文章评论 都整合到一个前端页面里
  • 分别开设路由和视图函数因为三个功能的逻辑性较强分开写比较好点

二、编写思路详细

1.开设一个全新的路由

path('<str:username>/article/<int:article_id>/', views.article_detail_func),

2.写视图函数

def article_detail_func(request, username, article_id):
    # 筛选谋篇具体的文章对象
    article_obj = models.Article.objects.filter(site__site_name=username).filter(pk=article_id).first()
    site_obj = models.Site.objects.filter(site_name=username).first()
    '''这里也可以添加健壮性校验 防止用户自己瞎传数据'''
    # 获取当前文章所有的评论数据
    comment_list = models.Comment.objects.filter(article=article_obj)
    return render(request, 'articleDetailPage.html', locals())

3.写文章详情页前端页面

这里一并写掉了文章的点赞点踩以及评论前端渲染页面

{% extends 'homePage.html' %}


{% block css %}
    <link rel="stylesheet" href="media/css/{{ site_obj.site_theme }}/">
    <style>
        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 125px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url('/static/img/upup.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url('/static/img/downdown.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }
    </style>
{% endblock %}

{% block title %}
    {{ site_obj.site_title }}
{% endblock %}

{% block content %}
    <div class="col-md-2">
        {% load mytag %}
        {% mymenu username %}
    </div>
    <div class="col-md-10">
        <h2 class="text-center">{{ article_obj.title }}</h2>
        {{ article_obj.content|safe }}
        {#            文章点赞点踩样式开始#}
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit upordown">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span>
                </div>
                <div class="buryit upordown">
                    <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span>
                </div>

                <div class="clear"></div>
                <span style="color: red" id="d1"></span>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>

        {#            文章点赞点踩样式结束#}
        
        {#        文章评论楼的渲染开始#}
        <div class="comment_list">
            <ul class="list-group">
                {% for comment_obj in comment_list %}
                    <li class="list-group-item">
                        <span><a href="#">#{{ forloop.counter }}楼</a></span>
                        <span>{{ comment_obj.comment_time|date:'Y-m-d H:i' }}</span>
                        <span><a href="/{{ comment_obj.user.username }}/">{{ comment_obj.user.username }}</a></span>
                        <p class="pull-right"><a href="#">引用&nbsp;&nbsp;</a></p>
                        <p class="pull-right"><a href="#" class="reply" username="{{ comment_obj.user.username }}" comment_id="{{ comment_obj.pk }}">回复&nbsp;&nbsp;</a></p>
                        <p>
                            {% if comment_obj.parent_id %}
                                @{{ comment_obj.parent.user.username }}
                            {% endif %}
                        </p>
                        <p>
                            {{ comment_obj.content }}
                        </p>
                    </li>
                {% endfor %}
            </ul>
        </div>
        {#        文章评论楼的渲染结束#}
        {#        文章评论样式开始#}
        {% if request.user.is_authenticated %}
            <div class="comment_area">
                <p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
                <textarea name="" id="comment" cols="30" rows="10" class="form-control"></textarea>
                <button class="btn btn-primary" id="commentBtn">提交评论</button>
            </div>
        {% else %}
            <p>
                <a href="/register/">注册</a>
                <a href="/login/">登录</a>
            </p>
        {% endif %}

        {#        文章评论样式结束#}
    </div>
{% endblock %}


{% block js %}
    <script>
        // 给点赞点踩图标绑定点击事件
        $('.upordown').click(function () {
            let currentEle = $(this);
            let isUp = $(this).hasClass('diggit')  // 判断标签是否含有某个class值 从而二选一区分赞和踩
            // 发送ajax请求
            $.ajax({
                url: '/up_or_down/', // 点赞点踩有一定的逻辑 单独开设接口处理
                type: 'post',
                data: {
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                    'article_pk': '{{ article_obj.pk }}',
                    'is_up': isUp,
                },
                success: function (args) {
                    if (args.code === 10000) {
                        currentEle.children().first().text(Number(currentEle.children().first().text()) + 1)

                    }
                    $('#d1').html(args.msg)
                }
            })
        })

        // 提前创建一个全局变量 用于存储评论主键值
        let parentId = null;
        // 给提交评论的按钮绑定点击事件
        $('#commentBtn').click(function () {
            // 获取用户评论的内容
            let commentMsg = $('#comment').val();
            let currentUserName = '{{ request.user.username }}';
            let oldCommentMsg = commentMsg;
            // 如果发送的是子评论 那么需要处理掉前缀内容(前端可以做 后端也可以做)
            if(parentId){
                commentMsg = commentMsg.slice(commentMsg.indexOf('\n') + 1)
            }
            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                    'article_pk': '{{ article_obj.pk }}',
                    'content': commentMsg,
                    'parent_id':parentId
                },
                success: function (args) {
                    if (args.code === 10000) {
                        // 清空评论框里面的内容
                        $('#comment').val('');
                        // 动态创建标签并添加到评论楼中
                        let tempComment = `
                        <li class="list-group-item">
                            <span class="glyphicon glyphicon-comment"><a href="/${currentUserName}/">${currentUserName}</a></span>
                            <p>
                                ${oldCommentMsg}
                            </p>
                        </li>
                        `
                        // 查找ul标签然后添加上述的标签即可
                        $('.list-group').append(tempComment)
                        // 清空全局变量
                        parentId = null;
                    }
                }
            })
        })

        // 给回复按钮绑定点击事件
        $('.reply').click(function () {
            // 获取回复按钮所在的评论用户名
            let targetUserName = $(this).attr('username');
            // 获取回复按钮所在的评论主键值 修改全局变量
            parentId = $(this).attr('comment_id');
            $('#comment').val('@' + targetUserName + '\n').focus();

        })
    </script>
{% endblock %}

4.开设文章点赞点踩以及评论路由

# 需要注意的是这两个功能属于逻辑比较简单功能,要放在动态匹配的路由的后面 要不然会出现被复杂路由拦截的情况
    # 文章点赞点踩
    path('up_or_down/', views.up_or_down_func),
    # 文章评论
    path('comment/', views.comment_func),

5.写点赞点踩后台逻辑

def up_or_down_func(request):
    """
    1.校验用户是否登录
    2.校验当前文章是否是当前用户自己的
    3.校验当前文章是否已经被当前用户点过
    4.创建点赞点踩记录(不要忘记文章表中的优化字段 同步自增)
    """
    print(request.POST)
    back_dict = {'code': 10000, 'msg': ''}
    if request.method == 'POST':
        if request.user.is_authenticated:
            article_pk = request.POST.get('article_pk')
            is_up = request.POST.get('is_up')  # true 普通的字符串
            article_obj = models.Article.objects.filter(pk=article_pk).first()
            if not article_obj.site.userinfo == request.user:
                is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
                if not is_click:
                    is_up = json.loads(is_up)  # 自动转换成python中布尔值
                    if is_up:
                        models.Article.objects.filter(pk=article_pk).update(up_num=F('up_num') + 1)
                        back_dict['msg'] = '点赞成功'
                    else:
                        models.Article.objects.filter(pk=article_pk).update(down_num=F('down_num') + 1)
                        back_dict['msg'] = '点踩成功'
                    models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                else:
                    back_dict['code'] = 10001
                    back_dict['msg'] = '您已经点过了'
            else:
                back_dict['code'] = 10002
                back_dict['msg'] = '你个臭不要脸的 不能给自己点'
        else:
            back_dict['code'] = 10003
            # from django.utils.safestring import mark_safe
            # back_dict['msg'] = mark_safe('请先<a href="/login/">登录</a>')
            back_dict['msg'] = '请先<a href="/login/">登录</a>'
        return JsonResponse(back_dict)

6.写文章评论的后台逻辑

@login_required
def comment_func(request):
    back_dict = {'code': 10000, 'msg': ''}
    if request.method == 'POST':
        article_pk = request.POST.get('article_pk')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')  # 直接获取即可 无需关系是否有值
        models.Article.objects.filter(pk=article_pk).update(comment_num=F('comment_num') + 1)
        models.Comment.objects.create(user=request.user, article_id=article_pk, content=content, parent_id=parent_id)
        back_dict['msg'] = '评论成功'
        return JsonResponse(back_dict)