• python 全栈开发,Day82(点赞和踩灭,用户评论)


    一、点赞和踩灭

    样式

    先来做样式,修改article_detail.html,增加div_digg的div

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ article_obj.content|safe }}
            </div>
            <div id="div_digg">
                <div class="diggit">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
    
    {% endblock %}
    View Code

    在static-->css目录下,创建文件article_detail.css

    #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/img/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/img/downdown.gif") no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    View Code

    从博客园拷贝2个图片到static-->img目录下

    修改base.html,引入article_detail.css

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="shortcut icon" href="https://common.cnblogs.com/favicon.ico" type="image/x-icon"/>
        {#公共样式#}
        <link rel="stylesheet" href="/static/css/theme/common.css">
        {#个人站点主题样式#}
        <link rel="stylesheet" href="/static/css/theme/{{ blog.theme }}">
        {#bootstrap#}
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
        <script src="/static/js/jquery.js"></script>
        <script src="/static/bootstrap/js/bootstrap.js"></script>
        {#文章详情#}
        <link rel="stylesheet" href="/static/css/article_detail.css">
    
    </head>
    <body>
    <div class="header">
        <p class="title">{{ blog.title }}</p>
    </div>
    
    
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-3">
                {#加载自定义标签模块#}
                {% load my_tags %}
                {#调用get_query_data标签,它返回left_region.html,是已经被渲染过的文件#}
                {% get_query_data username %}
            </div>
            <div class="col-md-9">
    
                {% block content %}
    
                {% endblock %}
    
            </div>
        </div>
    </div>
    
    </body>
    </html>
    View Code

    点击一篇文章:http://127.0.0.1:8000/xiao/articles/5/

    拉到最下面,右侧效果如下:

    绑定事件

    推荐和反对有2个按钮,要绑定2个事件吗?答案是可以,但是不推荐使用!为什么呢?

    因为会造成大量代码重复!这2个按钮需要发送的文章id和用户id是一样的,唯一不同的就是:一个是推荐,一个是反对。

    修改article_detail.html增加div,测试js代码

    注意:在base.html中已经导入了jquery

    hasClass() 方法检查被选元素是否包含指定的 class。它返回True和False

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                alert(is_up);
    
            })
        </script>
    
    {% endblock %}
    View Code

    刷新网页,点击文章右下角的推荐,提示true

    点击反对,提示false

    发送ajax请求

    数据分析

    到底是用get还是post呢?有一个规范:查询用get,更改用post

    查看blog_articleupdown表,这个是点赞表。插入一条记录,需要3个参数,分别是是否点赞、文章id、用户id

    注意:这个用户id是当前登录用户,不是文章作者!

    由于在视图函数中,有一个全局变量request.user。它是用户登录之后,才有的!所以这个用户id,不需要传。

    只需要传2个值就可以了,分别是是否点赞和文章id。

    在article_detail.html中,已经有一个article_obj,它是一个model对象,那么就可以得到文章id。

    是否点赞,通过js代码中的is_up变量,也可以知道。

    所以发送的数据,不需要使用标签选择器来获取。直接使用即可!

    操作流程

    1. 用户点击推荐或者反对,触发点击事件
    2. 发送ajax请求给服务器
    3. 服务器接收参数,生成一条点赞或者踩灭的记录
    4. 服务器将返回结果响应给浏览器,浏览器中的ajax代码的success中的data接收响应体
    5. ajax对响应的数据做判断,操作DOM,显示给用户看!

    服务器处理

    修改urls.py,增加一个路径digg

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        path('', views.index),
        #点赞或者踩灭
        path('digg/', views.digg),
    
        #文章详情
        re_path('(?P<username>w+)/articles/(?P<article_id>d+)/$', views.article_detail),
        # 跳转
        re_path('(?P<username>w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),
        # 个人站点
        re_path('(?P<username>w+)/$', views.homesite),
    ]
    View Code

    注意:digg要放到文章详情的上面

    修改article_detail.html

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    修改views.py,增加视图函数digg

    注意:导入2个模块

    import json
    from django.http import JsonResponse

    视图函数如下:

    from django.shortcuts import render,HttpResponse,redirect
    from django.contrib import auth
    from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown
    from django.db.models import Sum,Avg,Max,Min,Count
    from django.db.models import F
    import json
    from django.http import JsonResponse
    
    # Create your views here.
    def login(request):
    
        if request.method=="POST":
            user=request.POST.get("user")
            pwd=request.POST.get("pwd")
            # 用户验证成功,返回user对象,否则返回None
            user=auth.authenticate(username=user,password=pwd)
            if user:
                # 登录,注册session
                # 全局变量 request.user=当前登陆对象(session中)
                auth.login(request,user)
                return redirect("/index/")
    
        return render(request,"login.html")
    
    def index(request):
        article_list=Article.objects.all()
        return render(request,"index.html",{"article_list":article_list})
    
    def logout(request):  # 注销
        auth.logout(request)
        return redirect("/index/")
    
    def query_current_site(request,username):  # 查询当前站点的博客标题
        # 查询当前站点的用户对象
        user = UserInfo.objects.filter(username=username).first()
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象
        blog = user.blog
        return blog
    
    def homesite(request,username,**kwargs):  # 个人站点主页
        print("kwargs", kwargs)
    
        blog = query_current_site(request,username)
    
        # 查询当前用户发布的所有文章
        if not kwargs:
            article_list = Article.objects.filter(user__username=username)
        else:
            condition = kwargs.get("condition")
            params = kwargs.get("params")
            #判断分类、随笔、归档
            if condition == "category":
                article_list = Article.objects.filter(user__username=username).filter(category__title=params)
            elif condition == "tag":
                article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
            else:
                year, month = params.split("/")
                article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                      create_time__month=month)
        return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})
    
    def article_detail(request,username,article_id):
        blog = query_current_site(request,username)
    
        #查询指定id的文章
        article_obj = Article.objects.filter(pk=article_id).first()
        user_id = UserInfo.objects.filter(username=username).first().nid
    
        return render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})
    
    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
            else:
                #插入一条记录
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    json处理问题

    ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值

    json支持7种数据格式,其中true和false是其中2种。那么反序列化之后,会得到一个布尔值。

    为什么一定要反序列化呢?因为...create(...is_up=is_up) 等式右边的值,非空。那么ORM会认为是True!

    所以不管ajax传过来的是true和falase,它都是字符串,那么ORM执行时,始终都是True

    JsonResponse

    JsonResponse对象是HttpRespon的子类,它主要和父类的区别在于:

    1.它的默认Content-Type 被设置为: application/json

    2.第一个参数,data应该是一个字典类型,如果不是字典,抛出 TypeError的异常

    它返回json数据,那么ajax接收时,不需要反序列化,可以直接使用!

    刷新网页,点击右侧的推荐,查看浏览器控制台

    查看blog_articleupdown表记录,发现多了一条记录

    查看Pycharm控制台输出:

    <QueryDict: {'article_id': ['6'], 'csrfmiddlewaretoken': ['JgLyFpVgp92Rs8ppPCd2pm9jVj6z8bo9KSsMwKnakpB6CwTCT1K58v2JHLeR5ejN'], 'is_up': ['true']}>

    判断用户之前的操作

    当用户第一次点击推荐时,直接将数字加1。再次点击时,提示您已经推荐过!再次点击返回时,也提示您已经推荐过。

    同理,当用户对一篇点击返回后。之后不论是点击推荐还是反对,都会提示您已经反对过!

    不可以取消推荐或者反对!

    修改views.py,获取上一次操作的记录

    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                #插入一条记录
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    修改article_detail.html的js代码

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                            } else {
                                if (data.handled) {  //判断之前的操作记录
                                    $("#digg_tips").html("您已经推荐过!")
                                } else {
                                    $("#digg_tips").html("您已经反对过!")
                                }
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    修改static-->css目录下的article_detail.css,增加样式

    #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/img/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/img/downdown.gif") no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    .clear {
        clear: both;
    }
    
    #digg_tips{
        color: red;
    }
    View Code

    重启django项目,页面强制刷新几次!

    再次点击,提示已经点过了

    修改article_detail.html的js代码,增加动态效果,1秒后,清空红色文字

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                            } else {
                                if (data.handled) {  //判断之前的操作记录
                                    $("#digg_tips").html("您已经推荐过!")
                                } else {
                                    $("#digg_tips").html("您已经反对过!")
                                }
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    效果如下:

    更新文章表

    blog_articleupdown表有一个联合唯一索引,即使重复点击,也不会增加记录!

    blog_article表有一个up_count和down_count字段,分别表示推荐和反对的计数。

    那么这2个字段,也需要同时更新。在原有数值的基础上加1,需要使用F查询!

    导入F模块,完整代码如下:

    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                #插入一条记录
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                if is_up: # 判断为推荐
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                else: # 反对
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    清空blog_articleupdown表记录

    访问网页,重新点击。再次刷新页面,那么值就会更新了!

    网页实时展示

    上面已经实现了,点击之后,更新数据库。但是需要刷新网页,才会有效果。想要用户能实时看到数字,需要进行DOM操作。

    获取网页原有的值,进行加1。最后进行DOM操作,展示给用户看!

    修改article_detail.html的js代码

    注意:js是弱类型语言,获取到值时,不能直接进行加法,需要强制转换才行!

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this= $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val=_this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val)+1)
                            } else {
                                if (data.handled) {  //判断之前的操作记录
                                    $("#digg_tips").html("您已经推荐过!")
                                } else {
                                    $("#digg_tips").html("您已经反对过!")
                                }
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    清空blog_articleupdown表记录

    将blog_article表的相关字段设置为0

    访问网页,重新点击,效果如下:

    优化js代码

    将if判断部分,改成三元运算

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this = $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val = _this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val) + 1)
                            } else {
                                // 重复提交
                                var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                                $("#digg_tips").html(val);
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    事务

    是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。

    在上面的例子,点赞时,需要更新2个表。一个是blog_articleupdown表,一个是blog_article表。

    如果有一个表没有更新,那么就会产生脏数据!为了避免这个问题,需要使用事务!

    举例:模拟异常情况

    修改digg视图函数,模拟异常

    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                #插入一条记录
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                ask  # 这一行故意出错
                if is_up: # 判断为推荐
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                else: # 反对
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    清空blog_articleupdown表,将blog_article表的up_count和down_count字段设置为0

    刷新页面,重新点击,打开浏览器工具-->network

    出行了500错误

     查看blog_articleupdown表,多了一条记录

     

    查看blog_article表,对应文章的up_count字段,发现还是为0

    使用事务

    修改digg视图函数,导入模块transaction。

    使用语法很简单,将相关原子操作的代码直接tab一下,就可以了

    完整代码如下:

    from django.shortcuts import render,HttpResponse,redirect
    from django.contrib import auth
    from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown
    from django.db.models import Sum,Avg,Max,Min,Count
    from django.db.models import F
    import json
    from django.http import JsonResponse
    from django.db import transaction
    
    # Create your views here.
    def login(request):
    
        if request.method=="POST":
            user=request.POST.get("user")
            pwd=request.POST.get("pwd")
            # 用户验证成功,返回user对象,否则返回None
            user=auth.authenticate(username=user,password=pwd)
            if user:
                # 登录,注册session
                # 全局变量 request.user=当前登陆对象(session中)
                auth.login(request,user)
                return redirect("/index/")
    
        return render(request,"login.html")
    
    def index(request):
        article_list=Article.objects.all()
        return render(request,"index.html",{"article_list":article_list})
    
    def logout(request):  # 注销
        auth.logout(request)
        return redirect("/index/")
    
    def query_current_site(request,username):  # 查询当前站点的博客标题
        # 查询当前站点的用户对象
        user = UserInfo.objects.filter(username=username).first()
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象
        blog = user.blog
        return blog
    
    def homesite(request,username,**kwargs):  # 个人站点主页
        print("kwargs", kwargs)
    
        blog = query_current_site(request,username)
    
        # 查询当前用户发布的所有文章
        if not kwargs:
            article_list = Article.objects.filter(user__username=username)
        else:
            condition = kwargs.get("condition")
            params = kwargs.get("params")
            #判断分类、随笔、归档
            if condition == "category":
                article_list = Article.objects.filter(user__username=username).filter(category__title=params)
            elif condition == "tag":
                article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
            else:
                year, month = params.split("/")
                article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                      create_time__month=month)
        return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})
    
    def article_detail(request,username,article_id):
        blog = query_current_site(request,username)
    
        #查询指定id的文章
        article_obj = Article.objects.filter(pk=article_id).first()
        user_id = UserInfo.objects.filter(username=username).first().nid
    
        return render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})
    
    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                with transaction.atomic():
                    #插入一条记录
                    new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                    ask  # 模拟异常
                    if is_up: # 判断为推荐
                        Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                    else: # 反对
                        Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    清空blog_articleupdown表

    刷新页面,重新点击,再一次返回500错误

    查看blog_articleupdown表,发现并没增加一条记录。

     

    这样就比较合理了!删除异常代码

    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                with transaction.atomic():
                    #插入一条记录
                    new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                    if is_up: # 判断为推荐
                        Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                    else: # 反对
                        Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    View Code

    刷新页面,重新点击

    查看blog_articleupdown表,发现增加了一条记录。

    查看blog_article表,对应文章的up_count字段,发现为1了!

    事务用起来很简单,是因为django把复杂的逻辑给封装好了!
    有时间的小伙伴,还可以加一个需求,自己不能给自己点赞!

    二、用户评论

    评论样式

    先写html的样式代码

    修改article_detail.html

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
        <div class="clearfix"></div>
    
            <div class="comment">
                <p>评论列表</p>
                <ul class="comment_list list-group">
                    {% for comment in comment_list %}
                    <li class="list-group-item">
                       <div>
                           <a href="">#{{ forloop.counter }}楼</a>&nbsp;&nbsp;
                           <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                           <a href="">{{ comment.user.username }}</a>
                           <a href="" class="pull-right"><span>回复</span></a>
    
                       </div>
                       <div>
                           <p>{{ comment.content }}</p>
                       </div>
                    </li>
                    {% endfor %}
    
                </ul>
                <p>发表评论</p>
                <p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
                <div>
                    <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
                </div>
                <input type="button" value="submit" class="btn btn-default comment_btn">
            </div>
    
        </div>
        {% csrf_token %}
        <script>
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this = $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val = _this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val) + 1)
                            } else {
                                // 重复提交
                                var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                                $("#digg_tips").html(val);
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
        </script>
    
    {% endblock %}
    View Code

    修改css目录下的article_detail.css

    注意放一个gif图片到img目录下

    /*推荐和反对*/
    #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/img/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/img/downdown.gif") no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    .clear {
        clear: both;
    }
    
    #digg_tips{
        color: red;
    }
    
    /*评论*/
     input.author{
        background-image: url("/static/img/icon_form.gif");
        background-repeat: no-repeat;
        border: 1px solid #ccc;
        padding: 4px 4px 4px 30px;
         300px;
        font-size: 13px;
    }
    
    input.author {
        background-position: 3px -3px;
    }
    View Code

    刷新网页,拉到最下面,效果如下:

    评论步骤

    • 提交根评论请求
    • 显示根评论
    1. render
    2. Ajax显示
    • 提交子评论
    • 显示子评论
    • 评论树

    发送ajax请求

    查看评论表模型

    class Comment(models.Model):
        """
    
        评论表
    
        """
        nid = models.AutoField(primary_key=True)
        article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE)
        user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
        content = models.CharField(verbose_name='评论内容', max_length=255)
        create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
        parent_comment = models.ForeignKey(verbose_name="父级评论id", to='Comment',null=True, on_delete=models.CASCADE)
    View Code

    它有6个字段,其中nid、user、parent_comment、create_time是可选的。

    user 在视图函数中,可以直接获取,不需要ajax传此参数。

    parent_comment字段的值为空,表示这是一条根评论。否则为一条子评论!

    那么ajax如果传一个空值,表示为根评论,否则为子评论。

    create_time字段有一个属性:auto_now_add=True。

    字段在实例第一次保存的时候会保存当前时间,不管你在这里是否对其赋值。但是之后的save()是可以手动赋值的。

    也就是新实例化一个model,想手动存其他时间,就需要对该实例save()之后赋值然后再save()

    根评论

    修改article_detail.html的js代码,只需要发送3个参数即可。分别是content,atricle_id,parent_comment

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
            <div class="clearfix"></div>
    
            <div class="comment">
                <p>评论列表</p>
                <ul class="comment_list list-group">
                    {% for comment in comment_list %}
                        <li class="list-group-item">
                            <div>
                                <a href="">#{{ forloop.counter }}楼</a>&nbsp;&nbsp;
                                <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                                <a href="">{{ comment.user.username }}</a>
                                <a href="" class="pull-right"><span>回复</span></a>
    
                            </div>
                            <div>
                                <p>{{ comment.content }}</p>
                            </div>
                        </li>
                    {% endfor %}
    
                </ul>
                <p>发表评论</p>
                <p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                             value="{{ request.user.username }}"></p>
                <div>
                    <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
                </div>
                <input type="button" value="submit" class="btn btn-default comment_btn">
            </div>
    
        </div>
        {% csrf_token %}
        <script>
            // 点赞和踩灭
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this = $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val = _this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val) + 1)
                            } else {
                                // 重复提交
                                var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                                $("#digg_tips").html(val);
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
    
            // 提交评论
            $(".comment_btn").click(function () {
                {#评论内容#}
                var content = $("#comment_content").val();
                {#默认为空#}
                var pid = "";
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        content: content,
                        article_id: "{{ article_obj.pk }}",
                        pid: pid,
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        // 清空输入框的内容
                        $("#comment_content").val("")
                    }
                })
    
            })
        </script>
    
    {% endblock %}
    View Code

    修改urls.py,增加路径comment

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        path('', views.index),
        #点赞或者踩灭
        path('digg/', views.digg),
        # 评论
        path('comment/', views.comment),
    
        #文章详情
        re_path('(?P<username>w+)/articles/(?P<article_id>d+)/$', views.article_detail),
        # 跳转
        re_path('(?P<username>w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),
        # 个人站点
        re_path('(?P<username>w+)/$', views.homesite),
    ]
    View Code

    修改views.py,增加视图函数comment,完整代码如下:

    from django.shortcuts import render,HttpResponse,redirect
    from django.contrib import auth
    from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown,Comment
    from django.db.models import Sum,Avg,Max,Min,Count
    from django.db.models import F
    import json
    from django.http import JsonResponse
    from django.db import transaction
    
    # Create your views here.
    def login(request):
    
        if request.method=="POST":
            user=request.POST.get("user")
            pwd=request.POST.get("pwd")
            # 用户验证成功,返回user对象,否则返回None
            user=auth.authenticate(username=user,password=pwd)
            if user:
                # 登录,注册session
                # 全局变量 request.user=当前登陆对象(session中)
                auth.login(request,user)
                return redirect("/index/")
    
        return render(request,"login.html")
    
    def index(request):
        article_list=Article.objects.all()
        return render(request,"index.html",{"article_list":article_list})
    
    def logout(request):  # 注销
        auth.logout(request)
        return redirect("/index/")
    
    def query_current_site(request,username):  # 查询当前站点的博客标题
        # 查询当前站点的用户对象
        user = UserInfo.objects.filter(username=username).first()
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象
        blog = user.blog
        return blog
    
    def homesite(request,username,**kwargs):  # 个人站点主页
        print("kwargs", kwargs)
    
        blog = query_current_site(request,username)
    
        # 查询当前用户发布的所有文章
        if not kwargs:
            article_list = Article.objects.filter(user__username=username)
        else:
            condition = kwargs.get("condition")
            params = kwargs.get("params")
            #判断分类、随笔、归档
            if condition == "category":
                article_list = Article.objects.filter(user__username=username).filter(category__title=params)
            elif condition == "tag":
                article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
            else:
                year, month = params.split("/")
                article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                      create_time__month=month)
        return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})
    
    def article_detail(request,username,article_id):
        blog = query_current_site(request,username)
    
        #查询指定id的文章
        article_obj = Article.objects.filter(pk=article_id).first()
        user_id = UserInfo.objects.filter(username=username).first().nid
    
        return render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})
    
    def digg(request):
        print(request.POST)
        if request.method == "POST":
            #ajax发送的过来的true和false是字符串,使用json反序列化得到布尔值
            is_up = json.loads(request.POST.get("is_up"))
            article_id = request.POST.get("article_id")
            user_id = request.user.pk
    
            response = {"state": True, "msg": None}  # 初始状态
            #判断当前登录用户是否对这篇文章做过点赞或者踩灭操作
            obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
            if obj:
                response["state"] = False  # 更改状态
                response["handled"] = obj.is_up  # 获取之前的操作,返回true或者false
                print(obj.is_up)
            else:
                with transaction.atomic():
                    #插入一条记录
                    new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                    if is_up: # 判断为推荐
                        Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                    else: # 反对
                        Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)
    
            return JsonResponse(response)
    
        else:
            return HttpResponse("非法请求")
    
    def comment(request):
        print(request.POST)
        if request.method == "POST":
            # 获取数据
            user_id = request.user.pk
            article_id = request.POST.get("article_id")
            content = request.POST.get("content")
            pid = request.POST.get("pid")
            # 生成评论对象
            with transaction.atomic():  # 增加事务
                # 评论表增加一条记录
                comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
                # 当前文章的评论数加1
                Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
        
            response = {"state": False}  # 初始状态
            
            if comment.user_id:  # 判断返回值
                response = {"state": True}
            
            return JsonResponse(response)  # 返回json对象
        
        else:
            return HttpResponse("非法请求")
    View Code

    刷新网页,发表一条评论

     查看表blog_comment,多了一条记录

    查看blog_article表,对应的comment_count值,数值加1了。

     

    渲染评论

    修改article_detail视图函数,将评论列表传给模板

    def article_detail(request,username,article_id):
        blog = query_current_site(request,username)
    
        #查询指定id的文章
        article_obj = Article.objects.filter(pk=article_id).first()
        user_id = UserInfo.objects.filter(username=username).first().nid
    
        comment_list = Comment.objects.filter(article_id=article_id)
        dict = {"blog":blog,
                "username":username,
                'article_obj':article_obj,
                "user_id":user_id,
                "comment_list":comment_list,
                }
    
        return render(request,'article_detail.html',dict)
    View Code

    修改article_detail.html,使用for循环,遍历列表

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
            <div class="clearfix"></div>
    
            <div class="comment">
                <p>评论列表</p>
                <ul class="comment_list list-group">
                    {% for comment in comment_list %}
                        <li class="list-group-item">
                            <div>
                                <a href="">#{{ forloop.counter }}楼</a>&nbsp;&nbsp;
                                <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                                <a href="">{{ comment.user.username }}</a>
                                <a href="" class="pull-right"><span>回复</span></a>
    
                            </div>
                            <div>
                                <p>{{ comment.content }}</p>
                            </div>
                        </li>
                    {% endfor %}
    
                </ul>
                <p>发表评论</p>
                <p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                             value="{{ request.user.username }}"></p>
                <div>
                    <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
                </div>
                <input type="button" value="submit" class="btn btn-default comment_btn">
            </div>
    
        </div>
        {% csrf_token %}
        <script>
            // 点赞和踩灭
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this = $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val = _this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val) + 1)
                            } else {
                                // 重复提交
                                var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                                $("#digg_tips").html(val);
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
    
            // 提交评论
            $(".comment_btn").click(function () {
                {#评论内容#}
                var content = $("#comment_content").val();
                {#默认为空#}
                var pid = "";
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        content: content,
                        article_id: "{{ article_obj.pk }}",
                        pid: pid,
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        // 清空输入框的内容
                        $("#comment_content").val("")
                    }
                })
    
            })
        </script>
    
    {% endblock %}
    View Code

    刷新网页,效果如下:

    评论实时展示

    上面提交评论后,网页不能立即看到,需要刷新之后,才能看到。这样体验不好!

    查看博客园的评论,提交之后,会立即看到评论。此时不会显示楼层,只会显示评论内容!

    那么只需要提交成功之后,操作DOM,在评论列表中,追加一段li标签,展示一下,就可以了!

    数据获取问题

    那么内容从何而来呢?

    1.直接从html中获取相关数据

    2.让服务器返回相关数据,从响应体中取数据。

    针对这2种方式,我们选择第2种!

    为什么不选择第一种呢?因为第一种是原始输入框中的值,那么存储到数据后之后。就不一定还是输入框的值!

    服务器存储到数据库之前,会将提交的数据,做一次处理!

    我们想要的效果,就是不论是DOM操作,追加一段html代码。还是刷新网页,加载评论。这2种方式,评论内容是一摸一样的!

    所以,我们必须选择第二种方案,让服务器返回存储的值给ajax,ajax操作DOM,最加一段html代码,给用户展示!

    数据展示

    修改comment视图函数,返回3个变量给ajax

    def comment(request):
        print(request.POST)
        if request.method == "POST":
            # 获取数据
            user_id = request.user.pk
            article_id = request.POST.get("article_id")
            content = request.POST.get("content")
            pid = request.POST.get("pid")
            # 生成评论对象
            with transaction.atomic():  # 增加事务
                # 评论表增加一条记录
                comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
                # 当前文章的评论数加1
                Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
    
            response = {"state": False}  # 初始状态
    
            if comment.user_id:  # 判断返回值
                response = {"state": True}
            
            #响应体增加3个变量
            response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")
            response["content"] = comment.content
            response["user"] = request.user.username
            
            return JsonResponse(response)  # 返回json对象
    
        else:
            return HttpResponse("非法请求")
    View Code

    修改article_detail.html中的js代码,使用append最加一段li标签

    {% extends "base.html" %}
    
    {% block content %}
        <div class="article_info">
            <h4 class="text-center">{{ article_obj.title }}</h4>
            <div class="content">
                {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips">
                </div>
            </div>
            <div class="clearfix"></div>
    
            <div class="comment">
                <p>评论列表</p>
                <ul class="comment_list list-group">
                    {% for comment in comment_list %}
                        <li class="list-group-item">
                            <div>
                                <a href="">#{{ forloop.counter }}楼</a>&nbsp;&nbsp;
                                <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                                <a href="">{{ comment.user.username }}</a>
                                <a href="" class="pull-right"><span>回复</span></a>
    
                            </div>
                            <div>
                                <p>{{ comment.content }}</p>
                            </div>
                        </li>
                    {% endfor %}
    
                </ul>
                <p>发表评论</p>
                <p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                             value="{{ request.user.username }}"></p>
                <div>
                    <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
                </div>
                <input type="button" value="submit" class="btn btn-default comment_btn">
            </div>
    
        </div>
        {% csrf_token %}
        <script>
            // 点赞和踩灭
            $(".action").click(function () {
                {#hasClass() 方法检查被选元素是否包含指定的 class#}
                var is_up = $(this).hasClass("diggit");
                {#获取提示的span标签#}
                var _this = $(this).children("span");
                {#判断是否登录#}
                if ("{{ request.user.username }}") {
                    $.ajax({
                        url: "/digg/",
                        type: "post",
                        data: {
                            is_up: is_up,
                            article_id: "{{ article_obj.pk }}",
                            csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                        },
                        success: function (data) {
                            console.log(data);
                            console.log(typeof data);
                            if (data.state) {
                                //提交成功
                                var val = _this.text();  //获取text值
                                //在原有的基础上加1。注意:一定要进行类型转换
                                _this.text(parseInt(val) + 1)
                            } else {
                                // 重复提交
                                var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                                $("#digg_tips").html(val);
                                setTimeout(function () {
                                    $("#digg_tips").html("")  //清空提示文字
                                }, 1000)
                            }
    
                        }
                    })
                } else {
                    location.href = "/login/";
                }
    
            })
    
            // 提交评论
            $(".comment_btn").click(function () {
                {#评论内容#}
                var content = $("#comment_content").val();
                {#默认为空#}
                var pid = "";
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        content: content,
                        article_id: "{{ article_obj.pk }}",
                        pid: pid,
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        {#获取3个值#}
                        var comment_time = data.timer;
                        var comment_content = data.content;
                        var comment_user = data.user;
                        {#组织li标签#}
                        var $li = ` <li class="list-group-item">
                                           <div>
                                               <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                               <a href="">${comment_user}</a>
                                           </div>
                                           <div>
                                               <p>${comment_content}</p>
                                           </div>
                                        </li>`;
                        {#追加到评论列表中#}
                        $(".comment_list").append($li);
                        // 清空输入框的内容
                        $("#comment_content").val("")
                    }
                })
    
            })
        </script>
    
    {% endblock %}
    View Code

    刷新网页,重新评论,效果如下:

    评论可以实时展示了

    刷新网页,效果如下:

    显示出楼层

  • 相关阅读:
    入门系列4
    入门系列3
    入门系列2
    入门系列1
    sql进阶-筛选库表中数据为空的表
    sql进阶-删除所有的视图
    sql序列(2) sql语句功能表
    sql序列(5)事务
    sql序列(4)存储过程
    KVM虚拟化介绍
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9301423.html
Copyright © 2020-2023  润新知