zoukankan      html  css  js  c++  java
  • 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)
    
  • 相关阅读:
    JS 数组总结
    JS 数据类型及其判断
    CSS 优先级
    正则表达式及其使用例子
    常见的图片格式
    React 箭头函数的使用
    手动搭建 react+webpack 开发环境
    JS 函数参数及其传递
    JS 中的 this 指向问题
    JS 中函数的 length 属性
  • 原文地址:https://www.cnblogs.com/surpass123/p/13149952.html
Copyright © 2011-2022 走看看