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?"您已经推荐过!":"您已经反对过!";
    

      

  • 相关阅读:
    ASP.NET MVC 重点教程一周年版 第二回 UrlRouting
    ASP.NET MVC 重点教程一周年版 第三回 Controller与View
    DynamicData for Asp.net Mvc留言本实例 下篇 更新
    Asp.net MVC视频教程 18 单选与复选框
    使用ASP.NET MVC Futures 中的异步Action
    ASP.NET MVC RC 升级要注意的几点
    ATL、MFC、WTL CString 的今生前世
    msvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义
    关于Windows内存的一些参考文章
    Windows访问令牌相关使用方法
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9426091.html
Copyright © 2011-2022 走看看