BBS项目之点赞点踩功能

BBS项目之点赞点踩功能

需求分析

# 需求1
已经支持过的用户(点赞,点擦)不能再点
# 需求2
未经过认证的用户(匿名用户)不能点赞点踩,需要让其(登录或注册账号)
# 需求3
该文章的作者不能给自己的文章点赞

这里需要注意的是:点赞数点踩数是出现较为频繁的数据字段,但凡文章出现的地方,其也随之出现,为了避免频繁的跨表查询,对数据库查询进行优化,在Article 表中引入up_num,down_num。由此,引入了需求4。

# 需求4
文章表和点赞点踩表,需要同时进行数据更新,这里利用事务的一致性进行处理

文章表结构

class Article(models.Model):
    """文章表"""
    title = models.CharField(verbose_name='文章标题', max_length=64)
    desc = models.CharField(verbose_name='文章简介', max_length=255)
    # 文章内容有很多,一般使用TextField
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段优化设计(避免频繁的跨表查询操作)
    up_num = models.BigIntegerField(verbose_name='点赞数', default=0)
    down_num = models.BigIntegerField(verbose_name='点踩数', default=0)
    comment_num = models.BigIntegerField(verbose_name='评论数', default=0)

    # 外键字段
    # 一个站点有多篇文章(一对多)
    blog = models.ForeignKey(to='Blog', null=True)
    # 一个分类有多篇文章(一对多)
    category = models.ForeignKey(to='Category', null=True)
    # 一个标签下游多篇文章,一篇文章可以有多个标签(多对多)
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article', 'tag'),
                                  )

点赞点踩表结构

class UpAndDown(models.Model):
    """点赞点踩表"""
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    is_up = models.BooleanField()  # 传布尔值,数据库中存0/1

业务逻辑

前端:

# 点赞和点踩触发的是同一事件,通过ajax请求向后端提交数据;
# 点赞和点踩的不同之处在于,他们的布尔值不同,在数据库中存储为is_up(0/1);
# 当用户点完赞/踩后,前端页面是即时更新的,可以通过DOM操作,而不是render渲染。

后端:

# 匿名用户不支持点赞/点踩
# 文章作者不支持点赞/点踩
# 评论过的用户不支持点赞/点踩

基于以上的逻辑,进行如下的设计。

代码

前端

{# 点赞点踩开始 #}
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>
        {# 点赞点踩结束 #}

//给所有的action绑定事件
        $('.action').click(function () {
            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 === 1500) {
                        //将前端的数字加1
                        let old_num = $div.children().text();
                        $div.children().text(Number(old_num) + 1);
                        $('.diggword').html(args.msg)
                    } else (
                        $('.diggword').html(args.msg)
                    )
                }
            })
        });

后端

def up_or_down(request):
    if request.is_ajax():
        back_dict = {'code': 1500, 'msg': ''}
        # 判断当前用户是否登录
        if request.user.is_authenticated():
            article_id = request.POST.get('article_id')
            is_up = json.loads(request.POST.get('is_up'))
            article_obj = models.Article.objects.filter(pk=article_id).first()
            # 判断当前的文章是否是当前用户写的
            if not article_obj.blog.userinfo == request.user:
                # 校验当前用户是否已经点过了(哪个地方记录了用户到底点了还是没有点)
                is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
                if not is_click:
                    # 操作数据库来记录数据库   要同步article表中的普通字段
                    if is_up:
                        # 给点赞数加1
                        
                        
        # 利用F查询来获取表字段加1
        models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                        back_dict['msg'] = '点赞成功'
                    else:
                        # 给点踩数加1
                        models.Article.objects.filter(pk=article_id).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:
                    # 查询已经点过的是赞还是踩
                    up_or_down_obj = models.UpAndDown.objects.filter(user=request.user, article=article_obj).first()
                    once_click_up = up_or_down_obj.is_up
                    back_dict['code'] = 1501
                    back_dict['msg'] = '您已经支持过(赞)' if once_click_up else '您已经支持过(踩)'
            else:
                back_dict['code'] = 1502
                back_dict['msg'] = '不能给自己的文章投票'
        else:
            back_dict['code'] = 1503
            back_dict['msg'] = '<a href="/login/" style="text-decoration: none;color: red;">请先登录</a>'

        return JsonResponse(back_dict)
原文地址:https://www.cnblogs.com/surpass123/p/13149952.html