zoukankan      html  css  js  c++  java
  • 文章点赞功能(Ajax)

    一、文章点赞样式构建

    1、将base.html的css样式改为外部引入

      将base.html的内嵌样式删除,改为使用 HTML 头部的 <head> 标签对中使用<link>标签来引入外部的 CSS 文件。

    base.html内容如下所示:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 引入 Bootstrap 核心 CSS 文件 -->
        <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css">
        <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
        <script src="/static/js/jquery-3.3.1.js"></script>
        <!-- 引入 Bootstrap 核心 JavaScript 文件 -->
        <script src="/static/blog/bootstrap-3.3.7/js/bootstrap.js"></script> <!--依赖jquery-->
        <link rel="stylesheet" href="/static/blog/css/home_site.css">
        <link rel="stylesheet" href="/static/blog/css/article_detail.css">
    </head>
    <body>
    
    <div class="header">
        <div class="content">
            <!--站点标题-->
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <!--添加bootstrap面板-->
                {% load my_tags %}
                {% get_classification_style username %}
            </div>
            <div class="col-md-9">
                {% block content %}
    
                {% endblock %}
            </div>
        </div>
    </div>
    
    </body>
    </html>
    

     个人站点的样式——home_site.css:

    * {
        margin: 0;
        padding: 0;
    }
    .header {
         100%;
        height: 60px;
        background-color: #369;
    }
    .header .title {
        font-size: 18px; /* 字体大小 */
        font-weight: 100; /* 字体粗细 */
        line-height: 60px; /* 行高与页头一致,完成居中 */
        color: white;
        margin-left: 15px;
        margin-top: -10px;
    }
    .backend {
        float: right; /* 浮动到右边 */
        color: white;
        text-decoration: none; /* 去除下划线 */
        font-size: 16px;
        margin-right: 12px;
        margin-top: 10px;
    }
    .pub_info {
        margin-top: 10px;
        color: darkgray;
    }
    .menu {
        margin-top: 20px;
    }
    

    文章详情页的样式——article_detail.css:

    .article_info .title {
        margin-bottom: 20px;
    }
    

      上述css代码是将标题部分和文字主体部分错开20像素。

    2、构建点赞样式

      根据博客园代码,在article_detail.html引入文章推荐踩灭:

    {% extends "base.html" %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
        </div>
    
        {# 文章点赞 #}
        <div id="div_digg">
            <div class="diggit">
                <span class="diggnum" id="digg_count">1</span>
            </div>
            <div class="buryit">
                <span class="diggnum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>
    {% endblock %}
    

      将点赞的css样式写入article_detail.css中:

    .article_info .title {
        margin-bottom: 20px;
    }
    
    #div_digg {
        float: right;
        margin-bottom: 10px;
        margin-right: 30px;
        font-size: 12px;
         125px;
        text-align: center;
        margin-top: 10px;
    }
    
    /* 推荐 */
    .diggit {
        float: left;
         46px;
        height: 52px;
        background: url('/static/font/upup.gif') no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    
    /* 反对 */
    .buryit {
        float: right;
        margin-left: 20px;
         46px;
        height: 52px;
        background: url('/static/font/downdown.gif') no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    
    .clear {
        clear: both;  /* 清除浮动,解决塌陷问题 */
    }
    

      显示效果:

      

    二、文章点赞事件绑定 (Ajax)

      给推荐和反对的这两个标签绑定事件,一点击就发送ajax请求。

      另外查看blog_articleupdown表:

      

      其中is_up字段是存的是一个布尔值。点击推荐则为True,点击反对则为False.在这里将两个标签合在一个事件中,仅仅是做判断点击的是哪个标签。注意文章点赞的script代码应写在article_detail.html模板中。

      由于这两个标签有不同的类,一个包含diggit,一个包含buryit。因此只需要判断点击的标签class名就可以,article_detail.html:

     {% extends "base.html" %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
        </div>
    
        {# 文章点赞 #}
        <div id="div_digg">
            {# 推荐 #}
            <div class="diggit action">
                <span class="diggnum" id="digg_count">1</span>
            </div>
            {# 点灭 #}
            <div class="buryit action">
                <span class="diggnum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>
    
        <script>
            $('#div_digg .action').click(function () {
                var is_up = $(this).hasClass("diggit");
                alert(is_up);
            })
        </script>
    {% endblock %}
    

    三、文章点赞保存

    1、在article_detail.html模板初步构建ajax请求

    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
            $.ajax({
                url: "/digg/",
                type: "post",
                data: {
                    'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                    "is_up": is_up,
                    "article_id": "{{ article_obj.pk }}",
                },
                success: function (data) {
                    console.log(data);
                }
            })
        })
    </script>
    

      需要注意:文章点赞人、评论人都应是当前登录人。因此这里不需要传入user_id到ajax中。

      由于这里是post请求,因此需要添加csrf_token避免forbidden错误,因此需要构建一条新路由:

    urlpatterns = [
        path('admin/', admin.site.urls),
        ...
        path('digg/', views.digg),  # 点赞
      ... ]

      创建点赞视图函数:

    def digg(request):
        """
        点赞视图函数
        :param request:
        :return:
        """
        print(request.POST)
    
        return HttpResponse("OK")
    

      访问页面,点击推荐,视图函数request.POST输出:

    <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'], 'is_up': ['true'], 'article_id': ['1']}>
    

      同时页面控制台输出:OK。

    2、生成赞记录

    import json
    
    def digg(request):
        """
        点赞视图函数
        :param request:
        :return:
        """
        print(request.POST)
        # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
        #               'is_up': ['true'], 'article_id': ['1']}>
    
        article_id = request.POST.get("article_id")
        # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
        is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值
    
        # 点赞人即当前登录人
        user_id = request.user.pk
    
        # 创建一条新记录
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
        return HttpResponse("OK")
    

    在这里需要注意:

    (1)生成一条赞记录根据models中的类:

      

      因此拿到article_id、user_id、is_up这三条就可以生成一个新记录。

    (2)request.POST.get("is_up") 拿到的结果是一个字符串。因为它在js中虽然也是bool值,但是在传递时,没有设置ContentType,默认使用urlencoded编码,在组装键值的时候,is_up = True就按照字符串发送出去了。因此需要json来进行反序列化。

    (3)点击赞后,查看blog_articleupdwon表记录,确认新记录是否生成:

      

    四、文章点赞数的数据同步

      生成一条赞记录,就应该把赞记录对应的文章up_count+1,如果是踩灭,则将down_count+1;保持这种同步。

      涉及到up_count自加一,需要用到Django的F函数。

    import json
    
    from django.db.models import F   # F函数
    
    def digg(request):
        """
        点赞视图函数
        :param request:
        :return:
        """
        print(request.POST)
        # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
        #               'is_up': ['true'], 'article_id': ['1']}>
    
        article_id = request.POST.get("article_id")
        # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
        is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值
    
        # 点赞人即当前登录人
        user_id = request.user.pk
    
        # 创建一条新记录
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            queryset.update(up_count=F("up_count")+1)
        else:
            queryset.update(up_count=F("down_count")+1)
    
        return HttpResponse("OK")

      工作原理是,直接在数据库中查出数据,计数后更改数据库。点赞后刷新页面,点赞次数已经更新:

      

    五、点赞提示重复操作

      博客园文章点赞规则:用户对一篇文章点赞后,不允许再对文章进行点赞或踩灭操作。

    1、根据用户在点赞是否有记录进行判断

    import json
    from django.http import JsonResponse
    
    from django.db.models import F   # F函数
    
    def digg(request):
        """
        点赞视图函数
        :param request:
        :return:
        """
        print(request.POST)
        # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
        #               'is_up': ['true'], 'article_id': ['1']}>
    
        article_id = request.POST.get("article_id")
        # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
        is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值
    
        # 点赞人即当前登录人
        user_id = request.user.pk
    
        # 用户只要在点赞表存有记录就不能再存了
        obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
    
        response = {"state": True}
        if not obj:
            # 创建一条新记录
            ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
            queryset = models.Article.objects.filter(pk=article_id)
            if is_up:
                queryset.update(up_count=F("up_count")+1)
            else:
                queryset.update(down_count=F("down_count")+1)
        else:
            # 重复点赞提示,告诉ajax已经推荐过了
            response["state"] = False
            response["handled"] = obj.is_up   # True:推荐过了, Flase: 踩过了
    
        return JsonResponse(response)   # 返回response字典
    

      注意:

    (1)通过models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first(),拿到当前用户在点赞表中针对该文章的点赞记录。if not obj则可以判定没有点赞过,可以正常操作。

    (2)提前创建response字典,将需要传递给ajax的信息放入其中。然后引入JsonResponse传递字典。

    2、在ajax中处理重复错误信息

    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
            $.ajax({
                url: "/digg/",
                type: "post",
                data: {
                    'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                    "is_up": is_up,
                    "article_id": "{{ article_obj.pk }}"
                },
                success: function (data) {
                    console.log(data);
                    if (data.state) {
    
                    } else {
                        if (data.handled) {
                            $("#digg_tips").html("您已经推荐过!")
                        } else {
                            $("#digg_tips").html("您已经反对过!")
                        }
                        setTimeout(function () {
                            $("#digg_tips").html("")
                        }, 1000)
                    }
                },
            })
        })
    </script>
    

      注意:

    (1)在data.state为false时,判断data.handled字典是True/False,在id="digg_tips"标签添加对应的内容。

    (2)运用setTimeout函数,在提示消息显示1秒后,自动隐藏。

    (3)显示效果如下:

      

    六、点赞数ajax更新

      修改article_detail.html内script代码:

    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
            $.ajax({
                url: "/digg/",
                type: "post",
                data: {
                    'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                    "is_up": is_up,
                    "article_id": "{{ article_obj.pk }}"
                },
                success: function (data) {
                    console.log(data);
                    if (data.state) {
                        if (is_up){
                            var val = parseInt($("#digg_count").text());  // parseInt() 函数可解析一个字符串,并返回一个整数。
                            $("#digg_count").text(val+1);
                        } else {
                            var val = parseInt($("#bury_count").text());
                            $("#bury_count").text(val+1);
                        }
    
                    } else {
                        if (data.handled) {
                            $("#digg_tips").html("您已经推荐过!")
                        } else {
                            $("#digg_tips").html("您已经反对过!")
                        }
                        setTimeout(function () {
                            $("#digg_tips").html("")
                        }, 1000)
                    }
                },
            })
        })
    </script>
    

      这样在点击推荐或踩灭后,不用刷新页面,第一时间就显示了数字加1。

    七、代码优化

      主要针对article_detail.html中js的重复代码。

    {% extends "base.html" %}
    
    {% block content %}
        {%  csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
        </div>
    
        {# 文章点赞 #}
        <div id="div_digg">
            {# 推荐 #}
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            {# 点灭 #}
            <div class="buryit action">
                <span class="diggnum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>
    
    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
    
            $obj = $(this).children("span");
    
            $.ajax({
                url: "/digg/",
                type: "post",
                data: {
                    'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                    "is_up": is_up,
                    "article_id": "{{ article_obj.pk }}"
                },
                success: function (data) {
                    console.log(data);
                    if (data.state) {
                        var val = parseInt($obj.text());  // parseInt() 函数可解析一个字符串,并返回一个整数。
                        $obj.text(val+1);
                    } else {
                        // 三元表达式
                        var val = data.handled?"您已经推荐过!":"您已经反对过!";
                        $("#digg_tips").html(val);
    
                        setTimeout(function () {
                            $("#digg_tips").html("")
                        }, 1000)
                    }
                },
            })
        })
    </script>
    {% endblock %}
    

      注意:

    (1)$obj = $(this).children("span");  变量定义,取消了is_up的判断。

    (2)应用三元表达式true对应已经推荐过,false对应已经反对过

    // 三元表达式
    var val = data.handled?"您已经推荐过!":"您已经反对过!";
    

      

  • 相关阅读:
    Linux驱动之内核自带的S3C2440的LCD驱动分析
    【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问? Eureka 注册中心
    java GC算法 垃圾收集器
    Java嵌入式数据库H2学习总结(三)——在Web应用中嵌入H2数据库
    Java嵌入式数据库H2学习总结(二)——在Web应用程序中使用H2数据库
    Java嵌入式数据库H2学习总结(一)——H2数据库入门
    Java Master-Worker模式实现
    RSA公钥格式PKCS#1,PKCS#8互转(微信获取RSA加密公钥)
    自定义注解完成数据库切库(读写分离)
    Linux下CPU飘高定位
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9426091.html
Copyright © 2011-2022 走看看