点赞点踩及评论功能

文章点赞点踩

点赞点踩样式准备

'''
浏览器上你看到的花里胡哨的页面,内部都是HTML(前端)代码
我们搭建的个人站点里面的文章内容应当写html代码
1.如何拷贝文章
<div class="postBody">
    <div id="cnblogs_post_body" class="blogpost-body blogpost-body-html">...</div> == $05  # 将对应文章的这行代码拷贝到admin站点里面
2.如何拷贝点赞点踩
    1.拷贝前端点赞点踩图标 只拷了html
<div id="div_digg"></div> 2.css也要拷贝 由于有图片防盗链的问题 所以将图片直接下载到本地
'''

 

 

{% extends 'base.html' %}

{% block css %}
    <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/up.png) 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/down.png) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <h1>{{ article_obj.title }}</h1>
    <div class="article_content">
        {{ article_obj.content|safe }}
    </div>
    {#    点赞点踩样式开始#}
    <div>
        <div id="div_digg">
            <div class="diggit">
                <span class="diggnum" id="digg_count">0</span>
            </div>
            <div class="buryit">
                <span class="burynum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
    </div>
    {#    点赞点踩样式结束#}
{% endblock %}
artilce_detail.html

 

 

点赞点踩功能

"""
前端如何区分用户是点了赞还是点了踩
    1.给标签各自绑定一个事件
        两个标签对应的代码其实基本一样,仅仅是是否点赞点踩这一个参数不一样而已
    2.二合一
    给两个标签绑定一个事件
    //   给所有的action类绑定事件
      $('.action').click(function () {
            alert($(this).hasClass('diggit'))
        })

由于点赞点踩内部有一定的业务逻辑,所以后端单独开设视图函数处理
"""
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
from django.views.static import serve
from BBS import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    # 点赞点踩
    re_path(r'^up_or_down/', views.up_or_down,name='up_or_down'),
]
urls.py

 

{% extends 'base.html' %}

{% block css %}
    <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/up.png) 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/down.png) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <h1>{{ article_obj.title }}</h1>
    <div class="article_content">
        {{ article_obj.content|safe }}
    </div>
    {#    点赞点踩样式开始#}
    <div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">0</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red">
            </div>
        </div>
    </div>
    {#    点赞点踩样式结束#}
{% endblock %}

{% block js %}
    <script>
        // 给所有的action属性绑定点击事件
        $('.action').click(function(args){
            {#alert($(this).hasClass('diggit'))#}
            let isUp = $(this).hasClass('diggit');
            let $div = $(this);
            // 朝后端发送ajax请求
            $.ajax({
                url:'/up_or_down/',
                type:'post',
                data:{
                    'article_id':'{{ article_obj.pk }}',
                    'is_up':isUp,
                    'csrfmiddlewaretoken':'{{ csrf_token }}'
                },
                success:function (args){
                    if(args.code == 1000){
                        $('#digg_tips').text(args.msg)
                        // 将前端的数字加一
                        // 先获取前端数字
                        let oldNum = $div.children().text();  // 文本 是字符类型
                        // 易错点
                        $div.children().text(Number(oldNum) + 1)  // 字符串拼接了 1+1 = 11  11 + 1 = 111
                    }else{
                        $('#digg_tips').html(args.msg)
                    }
                }
            })

        })
    </script>
{% endblock %}
artilce_detail.html
import json
from django.db.models import F
def up_or_down(request):
    """
    1.校验用户是否登陆
    2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章)
    3.当前用户是否已经给当前文章点过了
    4.操作数据库
    :param request:
    :return:
    """
    if request.is_ajax():
        back_dic = {'code': 1000, 'msg': ''}
        # 1.校验用户是否登陆
        if request.user.is_authenticated:
            article_id = request.POST.get('article_id')
            is_up = request.POST.get('is_up')
            # print(is_up,type(is_up))  # true <class 'str'>
            is_up = json.loads(is_up)  # 易错点,记得转换
            # print(is_up,type(is_up))  # True <class 'bool'>
            # 2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章)  根据文章id查询文章对象,根据文章对象查作者,再与request.user对比
            article_obj = models.Article.objects.filter(pk=article_id).first()
            if not article_obj.blog.userinfo == request.user:
                # 3 校验当前用户是否已经点了  哪个地方记录了用户到底点没点
                is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
                if not is_click:
                    # 4 操作数据库 记录数据      要同步操作普通字段
                    # 判断当前用户点了赞还是踩 从而决定给哪个字段加一
                    if is_up:
                        # 给点赞数加一
                        models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                        back_dic['msg'] = '点赞成功'
                    else:
                        # 给点踩数加一
                        models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                        back_dic['msg'] = '点踩成功'
                    # 操作点赞点踩表
                    models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                else:
                    back_dic['code'] = 1001
                    back_dic['msg'] = '你已经点过了,不能再点了'  # 这里你可以做的更加的详细 提示用户到底点了赞还是点了踩
            else:
                back_dic['code'] = 1002
                back_dic['msg'] = '不能给自己点赞'
        else:
            back_dic['code'] = 1003
            back_dic['msg'] = '请先<a href="/login/">登陆</a>'
        return JsonResponse(back_dic)
views.py

 

 

文章评论

"""
我们先写根评论,再写子评论

根评论
    点击评论按钮需要将评论框里面的内容清空
    根评论有两步渲染方式
        1.DOM临时渲染
        2.页面刷新render渲染

子评论
    点击回复按钮发生了几件事
        1.评论框自动聚焦
        2.将回复按钮所在的那一行评论人的姓名拼接成@username
        3.评论框内部自动换行
    根评论子评论都是点击同一个按钮朝后端提交数据的
    parent_id
    根评论子评论区别在哪?
    parent_id
"""        

 

根评论

from django.contrib import admin
from django.urls import path,re_path
from app01 import views
from django.views.static import serve
from BBS import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    # 评论
    re_path(r'^comment/',views.comment),
]
urls.py

 

# 需要在article_detail函数里面如下代码
    # 获取当前文章所有的评论内容
    comment_list = models.Comment.objects.filter(article=article_obj)


from django.db import transaction
def comment(request):
    # 自己也可以给自己的文章评论内容
    if request.is_ajax():
        if request.method == 'POST':
            back_dic = {'code': 1000, 'msg': ''}
            if request.user.is_authenticated:
                article_id = request.POST.get('article_id')
                content = request.POST.get('content')
                # 直接操作评论表,存储数据(操作两张表)
                with transaction.atomic():
                    models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                    models.Comment.objects.create(user=request.user,article_id=article_id,content=content)
                back_dic['msg'] = '评论成功'
            else:
                back_dic['code'] = 1001
                back_dic['msg'] = '用户未登陆'
        return JsonResponse(back_dic)
views.py

 

{% block content %}
    {#    评论楼渲染开始#}
    {#    #1楼 2023-04-21 17:30 林黎尽致#}
    <div>
        <ul class="list-group">  <!--由于样式难看,这里使用了列表组-->
            {% for comment in comment_list %}
                <li class="list-group-item">
                    <span>#{{ forloop.counter }}楼</span>
                    <span>{{ comment.comment_time|date:'Y-m-d h:i:s' }}</span>
                    <span>{{ comment.user.username }}</span>
                    <span><a href="" class="pull-right">回复</a></span>
                    <div>
                        {{ comment.content }}
                    </div>
                </li>
            {% endfor %}
        </ul>
    </div>
    {#    评论楼渲染结束#}
    {#    文章评论样式开始#}
    {% if request.user.is_authenticated %}
        <div>
            <p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
            <div>
                <textarea name="comment" id="id_comment" cols="60" rows="10"></textarea>
            </div>
            <button class="btn btn-primary" id="id_submit">提交评论</button>
            <span style="color: red" id="errors"></span>
        </div>
    {% else %}
        <li><a href="{% url 'register' %}">注册</a></li>
        <li><a href="{% url 'login' %}">登陆</a></li>
    {% endif %}
    {#    文章评论样式结束#}

{% endblock %}
artilce_detail.html
{% block js %}
    <script>
        // 给所有的action属性绑定点击事件
        $('.action').click(function (args) {
            {#alert($(this).hasClass('diggit'))#}
            let isUp = $(this).hasClass('diggit');
            let $div = $(this);
            // 朝后端发送ajax请求
            $.ajax({
                url: '/up_or_down/',
                type: 'post',
                data: {
                    'article_id': '{{ article_obj.pk }}',
                    'is_up': isUp,
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (args) {
                    if (args.code == 1000) {
                        $('#digg_tips').text(args.msg)
                        // 将前端的数字加一
                        // 先获取前端数字
                        let oldNum = $div.children().text();  // 文本 是字符类型
                        // 易错点
                        $div.children().text(Number(oldNum) + 1)  // 字符串拼接了 1+1 = 11  11 + 1 = 111
                    } else {
                        $('#digg_tips').html(args.msg)
                    }
                }
            })

        })

        // 用户点击评论按钮朝后端发送ajax请求
        $('#id_submit').click(function () {
            // 获取用户评论的内容
            let conTent = $('#id_comment').val();
            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    'article_id': '{{ article_obj.pk }}',
                    'content': conTent,
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (args) {
                    if (args.code == 1000) {
                        $('#error').text(args.msg)

                        // 将评论框里面的内容清空
                        $('#id_comment').val('')

                        // 临时渲染评论楼
                        let userName = '{{ request.user.username }}';
                        let temp = `
                        <li class="list-group-item">
                            <span>${userName}</span>
                            <span><a href="#" class="pull-right">回复</a></span>
                            <div>
                                ${conTent}
                            </div>
                            </li>
                        `
                        // 将生成好的标签添加到ul标签内
                        $('.list-group').append(temp);
                        // 清空全局的parentId
                        parentId = null;
                    }
                }
            })
        })
    </script>
{% endblock %}
artilce_detail.js

 

 子评论

 

优化点

1.

 

在数据库存储数据时让其去掉@username

 

// 判断当前评论是否是子评论 如果是 需要将我们之前手动渲染的@username去除
            if(parentId){
                // 找到\n对应的索引 然后利用切片 但是前片顾头不顾尾 所以索引+1
                let indexNum = conTent.indexOf('\n') + 1;
                conTent = conTent.slice(indexNum)  // 将indexNum之前的所有数据切除 只保留后面的部分
            }

 

 

2.根评论与子评论在前端显示没有区别

 

{#                判断当前评论是否是子评论 如果是需要渲染对应的评论人名#}
                {% if comment.parent_id %}
                    <p>@{{ comment.parent.user.username }}</p>
                {% endif %}

 

3.

 

{% block content %}
{#    评论楼渲染开始#}
{#    #3楼 2020-05-14 14:11 代码一字狂#}
    <div>
    <ul class="list-group">
        {% for comment in comment_list %}
             <li class="list-group-item">
            <span>#{{ forloop.counter }}楼</span>
            <span>{{ comment.comment_time|date:'Y-m-d h:i:s' }}</span>
            <span>{{ comment.user.username }}</span>
            <span><a class="pull-right reply" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}">回复</a></span>
            <div>
{#                判断当前评论是否是子评论 如果是需要渲染对应的评论人名#}
                {% if comment.parent_id %}
                    <p>@{{ comment.parent.user.username }}</p>
                {% endif %}
                    {{ comment.content }}
            </div>
            </li>
        {% endfor %}
</ul>

        
    </div>
{#    评论楼渲染结束#}
    {#   文章评论样式开始 #}
    {% if request.user.is_authenticated %}
        <div>
        <p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
        <div>
            <textarea name="comment" id="id_comment" cols="60" rows="10" ></textarea>
        </div>
        <button class="btn btn-primary" id="id_submit">提交评论</button>
        <span style="color: red" id="errors"></span>
    </div>
        {% else %}
                <li><a href="{% url 'reg' %}">注册</a></li>
                <li><a href="{% url 'login' %}">登陆</a></li>
    {% endif %}
    {#   文章评论样式结束 #}
{% endblock %}
article_detail.html
{% block js %}
    <script>
        // 设置一个全局的parentID字段
        let parentId = null;
        // 用户点击评论按钮朝后端发送ajax请求
        $('#id_submit').click(function () {
            // 获取用户评论的内容
            let conTent = $('#id_comment').val();
            // 判断当前评论是否是子评论 如果是 需要将我们之前手动渲染的@username去除
            if(parentId){
                // 找到\n对应的索引 然后利用切片 但是前片顾头不顾尾 所以索引+1
                let indexNum = conTent.indexOf('\n') + 1;
                conTent = conTent.slice(indexNum)  // 将indexNum之前的所有数据切除 只保留后面的部分
            }
            $.ajax({
                url:'/comment/',
                type:'post',
                data:{
                    'article_id':'{{ article_obj.pk }}',
                    'content':conTent,
                    // 如果parantId没有值 那么就是null 后端存储null没有任何关系
                    'parent_id':parentId,
                    'csrfmiddlewaretoken':'{{ csrf_token }}'
                },
                success:function (args) {
                    if(args.code ==1000){
                        $('#error').text(args.msg)

                        // 将评论框里面的内容清空
                        $('#id_comment').val('');

                        // 临时渲染评论楼
                        let userName = '{{ request.user.username }}';
                        let temp = `
                        <li class="list-group-item">

                            <span>${userName}</span>
                            <span><a href="#" class="pull-right">回复</a></span>
                            <div>
                                ${conTent}
                            </div>
                            </li>
                        `
                        // 将生成好的标签添加到ul标签内
                        $('.list-group').append(temp);
                        // 清空全局的parentId
                        parentId = null;
                    }
                }
            })
        })

    // 给回复按钮绑定点击事件
    $('.reply').click(function () {
        // 需要评论对应的评论人姓名   还需要评论的主键值
        // 获取用户名
        let commentUserName = $(this).attr('username');
        // 获取主键值 直接修改全局
        parentId = $(this).attr('comment_id');
        // 拼接信息塞给评论框
        $('#id_comment').val('@' + commentUserName + '\n').focus()
    })
    </script>
{% endblock %}
article_detail.js

 

from django.db import transaction
def comment(request):
    # 自己也可以给自己的文章评论内容
    if request.is_ajax():
        if request.method == 'POST':
            back_dic = {'code': 1000, 'msg': ''}
            if request.user.is_authenticated:
                article_id = request.POST.get('article_id')
                content = request.POST.get('content')
                parent_id = request.POST.get('parent_id')
                # 直接操作评论表,存储数据(操作两张表)
                with transaction.atomic():
                    models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                    models.Comment.objects.create(user=request.user,article_id=article_id,content=content,parent_id=parent_id)
                back_dic['msg'] = '评论成功'
            else:
                back_dic['code'] = 1001
                back_dic['msg'] = '用户未登陆'
        return JsonResponse(back_dic)
views.py

 

posted @ 2023-04-22 00:24  猿小姜  阅读(152)  评论(1编辑  收藏  举报