• django框架之BBS项目之文章详情和点赞


    内容回顾
        1. 个人博客主页
            1. 分类展示
                - 文章分类(2)
                    文章(Article)表和分类(Category)表
                    1. 先找这个人的博客有哪些文章分类
                    2. 每个文章分类下的文章总数
                    3. 基于对象的查询 category_obj.article_set.all().count()
                    
                - 文章标签
                    文章(Article)表和标签(Tag)表
                    1. 先找这个人的博客有哪些文章标签
                    2. 每个文章标签下的文章总数
                    3. 基于对象的查询 tag_obj.article_set.all().count()
                - 日期归档
                    1. MySQL日期格式化函数          --> MySQL内置的那些函数
                       DATE_FORMAT(字段,'格式')    --> sqlite语法:strftime('格式', '字段')
                       
                    2. Django ORM执行原生SQL语句的方式:
                        1. .extra()
                            ret = models.Article.objects.all().extra(
                                select={"create_ym": "DATE_FORMAT(create_time, '%%Y-%%m-%%d')"}
                            )
                            # models.Article.objects.all()  --> [ArticleObj1, ArticleObj2, ...]
                            # .extra(select={"create_ym": "DATE_FORMAT(create_time, '%%Y-%%m-%%d')"})
                            #     --> ArticleObj1.create_ym
                        2. 类似pymysql的用法
                            # 更高灵活度的方式执行原生SQL语句
                            from django.db import connection  # 连接
                            # 从连接中获取光标,等待输入命令
                            cursor = connection.cursor()
                            # 光标执行SQL语句
                            cursor.execute("""SELECT DATE_FORMAT(create_time, '%Y-%m') FROM blog_article;""")
                            # 取出SQL语句执行后的结果
                            ret = cursor.fetchall()
                            print(ret)
                    
                    3. 使用ORM按照年月分组,分别统计出年月的的文章数
                        archive_list = models.Article.objects.filter(user=user_obj).extra(
                            select={"y_m": "DATE_FORMAT(create_time, '%%Y-%%m')"}
                        ).values("y_m").annotate(c=Count("id")).values("y_m", "c")
                                    
            2. 4合1路由实现分类访问文章
                1. URL设计
                    注意事项:Django的urls.py中分组命名和分组匹配不能混合使用!!!
                    
                2. 视图中利用args去判断    
                    
        3. 补充
            1. QuerySet是惰性求值的
            2. debug-tool-bar工具的使用:https://www.cnblogs.com/liwenzhou/p/9245507.html

    今天我们实现文章的详情页面,就是当我们进入个人博客页面后,点击谋篇具体的文章,展示相应的文章详情。

    我们就需要给文章详情配url,blog/yangbo/p/1,

    所以a标签的地址就配成:

     <a href="/blog/{{ user_obj.username }}/p/{{ article.id }} "><h4 class="media-heading">{{ article.title }}</h4></a>

    url.py文件就配成:

    url(r'^blog/(w+)/p/(d+)/$',views.article),

    视图函数:

    def article(request,username,id):
        '''
        文章详情页面
        :param request: 请求对象
        :param username:用户名
        :param id:id
        :return:
        '''
        user_obj = models.UserInfo.objects.filter(username=username).first()
        blog = user_obj.blog
        category_list = models.Category.objects.filter(blog=blog)
        tag_list = models.Tag.objects.filter(blog=blog)
        article_list = models.Article.objects.filter(user=user_obj)
        archive_list = models.Article.objects.filter(user=user_obj).extra(
            select={'y_m': 'DATE_FORMAT(create_time,"%%Y-%%m")'}
        ).values('y_m').annotate(c=Count('id')).values('y_m', 'c')
        article_obj = models.Article.objects.filter(id = id).first()
        print(article_obj.title)
        return render(request,'article.html',{
            'article_obj':article_obj,
            'article_list': article_list,
            'user_obj': user_obj,
            'category_list': category_list,
            'tag_list': tag_list,
            'archive_list': archive_list
        })

    这样我们发现页面有很多重复的代码:因为每个页面都基本一样只是文章展示区不一样,所以我们就想到了模板将一样的地方放在一个模板里。

    新建一个html文件,将所有一样的代码放在base.html里,在模板页面中不一样的地方定义一个块

    {% block page-main %}

    {% endblock page-main %}

    然后再子页面中:

    {% extends 'base.html' %}
    {% block page-main %}
    写不一样的代码,这里的代码会替换base模板中block的内容。
    {% endblock %}

    在视图函数中,不管是在个人博客的视图函数还是在文章详情的试图函数中,我们都会看到很多重复的代码,首先我们想到的是把重复的代码写成一个函数,用这个函数的时候,调用就可以了,但是,如果这个函数除了问题,那么调用它的试图函数就不会执行页面就不会展示。这块有点难理解,可以复习一下模板和组件那块地内容!!!

    这时候我们想到了组件:

    在app的文件夹下创建一个名为templatetags(名字必须是templatetags)的package,在里面创建一个py文件,

    在文件里必须写上这两行代码:

    from django import template
    # 实例必须叫这个名字
    register = template.Library()

    然后再写组件的内容:

    from django import template
    from blog import models
    from django.db.models import Count
    
    #实例必须叫这个名字
    register = template.Library()
    
    @register.inclusion_tag(filename='left_menu.html')
    def left_con(username):
        print(username)
        user_obj = models.UserInfo.objects.filter(username=username).first()
        print(user_obj)
        blog = user_obj.blog
        category_list = models.Category.objects.filter(blog=blog)
        tag_list = models.Tag.objects.filter(blog=blog)
        archive_list = models.Article.objects.filter(user=user_obj).extra(
            select={'y_m': 'DATE_FORMAT(create_time,"%%Y-%%m")'}
        ).values('y_m').annotate(c=Count('id')).values('y_m', 'c')
        return {
            'user_obj':user_obj,
            'category_list': category_list,
            'tag_list': tag_list,
            'archive_list': archive_list
        }

    bas.html页面中应该这样引入组件:

      {% load left_memu %}
    {
    % left_con user_obj.username %}

    base.html中引入left_memu函数,并且将user_obj.username传给这个left_con函数,left_con将返回值传给left_menu.html页面渲染,渲染完了之后,然后在把整个left_menu.html页面在base.html页面中渲染。

    接下来就是给文章详情添加点赞的功能:

    当我们点击点赞或反对时 ,我们应该拿到的是文章的id,和用户名的id,然后把这些数据传给后端,后端在数据库中做记录,然后将数据在页面中渲染。

    html代码:

    <!--点赞开始-->
        <div id="div_digg">
            <div class="diggit digg">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit digg">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <!--提示信息-->
            <div class="diggword" id="digg_tips">
    
            </div>
        </div>
    <!--点赞结束-->
    $('.digg').click(function () {
                if(!'{{ request.user.username }}'){ //如果不加引号,就会被当成是变量
                    //先判断有没有登陆,没有登陆就跳转到登陆页面
                    location.href = '/login/?next={{ request.get_full_path }}'
                }
                //已经登陆就可以点赞或反对
                var userid = '{{ request.user.id }}';
                var articleid = '{{ article_obj.id }}';
                //区分点赞还是反对
                var isup = $(this).hasClass('diggit');
                // 像后端发请求
                $.ajax({
                    url:'/up_down/',
                    type:'post',
                    data:{
                        csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val(),
                        userid:userid,
                        articleid:articleid,
                        isup:isup,
                    },
                    success:function (res) {
                        if(res.code!==0){
                            //只需要把错误提示显示出来就可以
                            $('.diggword').text(res.msg)
                        }else{
                            //1.更新点赞数或反对数
                            if(isup){
                                //点赞数加一
                                var $UpSpan = $("#digg_count");
                                $UpSpan.text(+$UpSpan.text()+1);
                                $('.diggword').text(res.msg)
                            }else{
                                // 反对数加一
                                var $downSpan = $("#bury_count");
                                $downSpan.text(+$downSpan.text()+1);
                                // 打印提示信息 
                                $('.diggword').text(res.msg)
                            }
                        }
                    }
                })
            });

    views代码:

    def up_down(request):
        if request.method == 'POST':
            print(request.POST)
            res={'code':0}
            userid = request.POST.get('userid')
            articleid = request.POST.get('articleid')
            isup = request.POST.get('isup')
            isup = True if isup.upper() == 'TRUE' else False
            # 5. 不能给自己点赞
            is_own_article= models.Article.objects.filter(user_id=userid,id=articleid).first()
            print(is_own_article)
            if is_own_article:
                res['code'] = 1
                res['msg'] = '不能给自己点赞或反对'
            else:
                # 3.同一个人只能给同一篇文章点赞一次
                # 4. 点赞和反对两个只能选一个
                # 判断一下 当前这个人和这篇文章在点赞表里有没有记录
                is_exit = models.ArticleUpDown.objects.filter(user_id=userid,article_id=articleid).first()
                # 如果存在
                if is_exit:
                    res['code'] = 1
                    # 表示已经点赞过
                    if is_exit.is_up == True:
                        res['msg'] = '已经点赞过了'
                    # 表示已经反对过
                    else:
                        res['msg'] = '已经反对过了'
                else:
                    from django.db import transaction
                    from django.db.models import Count, F
                      # 事务操作
                    with transaction.atomic():
                        # 1.先创建点赞记录
                        models.ArticleUpDown.objects.create(user_id=userid,article_id=articleid,is_up=isup)
                        # 2.在更新文章表
                        if isup:
                            # 点赞数加1
                            models.Article.objects.filter(id=articleid).update(up_count=F('up_count')+1)
                            res['msg'] = '点赞成功'
                        else:
                            #反对数加1
                            models.Article.objects.filter(id=articleid).update(down_count=F('down_count')+1)
                            res['msg'] = '反对成功'
            return JsonResponse(res)
  • 相关阅读:
    黑松白鹿
    跨越
    第三年
    Lua windows环境搭建
    Iron man
    水果沙拉
    六周岁
    sqlserver数据库附加报错5120
    [BeiJing2006]狼抓兔子 平面图最小割
    BZOJ2118: 墨墨的等式 思维建图
  • 原文地址:https://www.cnblogs.com/yb635238477/p/9495467.html
Copyright © 2020-2023  润新知