BBS项目-点赞点踩功能

1 添加文章内容

先登录admin后台,给文章详情页加入文章内容。

浏览器上你看到的花里胡哨的页面,内部都是HTML(前端)代码。那我们的文章内容应该写什么???直接拷贝博客园中文章的html代码,copy outerhtml

添加完文章内容后,在article_detail.html 页面,要进行转义,前端才能显示出文章内容

{% extends 'base.html' %}

{% block content %}
    <h1>{{ article_obj.title }}</h1>
    <div class="content">
        {{ article_obj.content|safe}}
    </div>
{% endblock %}

 

2 拷贝点赞点踩样式

每篇文章详情下都有点踩点踩功能,因此我们要在详情页加上。

2.1 拷贝前端点赞点踩图标 

article_detail.html 

{% block content %}
    <h1>{{ article_obj.title }}</h1>
    <div class="content">
        {{ article_obj.content|safe }}
    </div>
    {# 点赞点踩开始 #}
    <div>
        <div id="div_digg">
            <div class="diggit" onclick="votePost(17047871,'Digg')">
                <span class="diggnum" id="digg_count">3</span>
            </div>
            <div class="buryit" onclick="votePost(17047871,'Bury')">
                <span class="burynum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
    </div>
    {# 点赞点踩结束 #}
{% endblock %}

2.1 拷贝前端点赞点踩css

仅仅拷贝了html 并没有点踩点踩的样式,我们还需要拷贝css代码

注意 digg 这个div 标签嵌套的几个标签的样式全部都要拷贝。

在base.html 模板页面,开一个css 的block

<head>
    <meta charset="UTF-8">
    {% load static %}
    <script src="{% static 'jQuery-3.6.0.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/mysetup.js' %}"></script>
    {% block css %}

    {% endblock %}
    <title>Title</title>
</head>

在 article_detail.html 页面,就能写自己的css样式,把拷贝的点赞点踩css样式放到 block 块中的style标签中。

{% 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.cnblogs.com/images/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.cnblogs.com/images/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 %}

由于有图片防盗链的问题,所以将图片直接下载到本地,url 改为本存放地址

.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;
}

 

3 点赞点踩逻辑

3.1 前端如何区分用户是点了赞还是点了踩

方式一:给点赞和点踩标签各自绑定一个事件,但两个标签对应的代码基本一样,仅仅是是否点赞点踩这一个参数不一样,写两个点击事件发送ajax请求,代码冗余。

方式二:给两个标签绑定一个事件,如下:

{# 点赞点踩开始 #}
    <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">
            </div>
        </div>
    </div>
    {# 点赞点踩结束 #}
{% endblock %}

{% block js %}  //在base.html开了一个js的block块,在 article_detail.html 页面,就能写自己的js代码
    <script>
        $('.action').click(function () {
            alert($(this).hasClass('diggit'))
        })
    </script>
{% endblock %}

给两个标签都添加了action类,给所有的action类绑定点击事件, $(this)指代当前标签对象,点击的谁就代表谁,通过hasClass获取标签属性,如果获取到diggit属性,返回true,说明是点赞;反之,返回false,说明是点踩。

3.2 ajax请求

点击标签朝后端发送ajax请求,由于点赞点踩内部有一定的业务逻辑,所以后端单独开设视图函数处理。

点赞点踩的 url 放在个人站点与文章详情的url前面,因为这几个url有正则匹配,防止被个人站点或文章详情 url 截获。

path('up_or_down/', views.up_or_down),

re_path(r'^(?P<username>\w+)/$', views.site, name='site'),

re_path(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.site),

re_path(r'^(?P<username>\w+)/article/(?P<article_id>\d+)', views.article_detail),

ajax朝点赞点踩视图发送请求,请求数据呢? 点赞点踩表要记录的是哪个用户哪篇文章点了还是点了。点赞点踩视图的业务逻辑,首先是登录用户才能操作,后端能够获取到用户,因此ajax只用发送 文章id 和 点赞或是点踩(用布尔值表示)。

% block js %}
    <script>
        $('.action').click(function () {
            let isUp = $(this).hasClass('diggit')  //返回布尔值赋值给变量isUp
            $.ajax({
                url:'/up_or_down/',
                type: 'post',
                data: {
                    article_id: '{{ article_obj.pk }}',  //文章详情页通过模板语法获取到文章id
                    is_up: isUp                 
                },
                success: function (args) {
                    alert(args)
                }
            })
        })
    </script>
{% endblock %}

3.3 后端视图函数逻辑

    1.校验用户是否登陆
    2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章)
    3.当前用户是否已经给当前文章点过了
    4.操作数据库

先写出正确的业务逻辑:

def up_or_down(request):
    # 点赞点踩视图只接收ajax请求
    if request.is_ajax():
        back_dic = {'code': 0, 'msg': ''}
        # 1 校验用户是否登录
        if request.user.is_authenticated:
            article_id = request.POST.get('article_id')
            is_up = json.loads(request.POST.get('is_up'))  # 前端传过来的是json格式字符串(true/false),反序列化为布尔值

            # 2 判断当前文章是否是用户自己写的
            if models.Article.objects.filter(Q(pk=article_id), ~Q(blog__userinfo=request.user)):
                # 3 判断当前用户是否已经给当前文章点过了;点赞点踩表里筛选,同时符合的数据说明当前这个用户给当前这个文章点过了
                # 虚拟字段传对象,真实字段传被关联表的主键值
                if not models.UpAndDown.objects.filter(user=request.user, article_id=article_id):
                    # 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'] = '点踩成功'
                    # 5 操作点踩点踩表录入数据
                    models.UpAndDown.objects.create(user=request.user, article_id=article_id, is_up=is_up)

        return JsonResponse(back_dic)

第二个判断用了Q查询,筛选条件是文章 id当前用户取反,如果能取到对象,说明当前文章不是当前用户自己写的。

或者直接根据文章id和当前用户取文章对象,再取反操作。

if not models.Article.objects.filter(pk=article_id, blog__userinfo=request.user):

或者根据文章id查询文章对象,根据文章对象查到作者,判断作者跟request.user是不是同一个人。

article_obj = models.Article.objects.filter(pk=article_id).first()

if not article_obj.blog.userinfo == request.user:

补全错误逻辑:

第一个判断用户登录的逻辑,返回错误信息,给“登录”套一个a标签,可以点击跳转到登录页面

def up_or_down(request):
    if request.is_ajax():
        back_dic = {'code': 0, 'msg': ''}
        if request.user.is_authenticated:
            article_id = request.POST.get('article_id')
            is_up = json.loads(request.POST.get('is_up'))  

            if models.Article.objects.filter(Q(pk=article_id), ~Q(blog__userinfo=request.user)):
                if not models.UpAndDown.objects.filter(user=request.user, article_id=article_id):
                    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_id=article_id, is_up=is_up)
                else:
                    back_dic['code'] = 1
                    if models.UpAndDown.objects.filter(user=request.user, article_id=article_id, is_up=1):
                        back_dic['msg'] = '你已经点过赞了'
                    else:
                        back_dic['msg'] = '你已经点过踩了'
            else:
                back_dic['code'] = 1
                back_dic['msg'] = '不能点赞或点踩自己的文章'
        else:
            back_dic['code'] = 1
            back_dic['msg'] = '请先<a href="/login/">登录</a>'

        return JsonResponse(back_dic)

3.4 前端信息提示

后端返回back_dic后,前端根据code判断,提示正确或错误的信息。

上图中,点赞点踩显示的数字是固定写死的,我们要实现点赞点踩过后,前端数字实时变动,即动态展示文章表中up_num和down_num两个字段。

ajax请求如下

{% block js %}
    <script>
        $('.action').click(function () {
            let isUp = $(this).hasClass('diggit')  //返回布尔值赋值给变量isUp
            let $div = $(this)                     //把当前操作标签赋值给变量$div
            $.ajax({
                url:'/up_or_down/',
                type: 'post',
                data: {
                    article_id: '{{ article_obj.pk }}',
                    is_up: isUp
                },
                success: function (args) {
                    if (args.code===0){
                        $('#digg_tips').text(args.msg)             //点赞或点踩成功,返回信息提示
                        let oldNum = $div.children().text();       //获取当前标签之前的点赞数或是点踩数
                        $div.children().text(Number(oldNum) + 1)   //点赞点踩成功后把数字加1,oldNum是字符串,要先转成数字类型再加1
                    }else {
                        $('#digg_tips').html(args.msg)           //返回错误信息提示,未登录的情况后端返回的a标签,因此用html显示
                    }
                }
            })
        })
    </script>
{% endblock %}

 

posted @   不会钓鱼的猫  阅读(202)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示