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)

猜你喜欢

转载自www.cnblogs.com/yb635238477/p/9495467.html