Django学习 day75之BBS项目day4

一 侧边栏展示

三个分组查询

# 按分类查询文章数
res_category =models.Category.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list('name', 'num', 'id')

# 按标签查询文章数
res_tag =models.Tag.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list('name','num', 'id')

# 按时间对文件进行归档
res_month =models.Article.objects.filter(blog=user.blog).annotate(month=TruncMonth('create_time')).values(
'month').annotate(c=Count('id')).order_by('-month').values_list('month', 'c')

根据年月分组的分析过程

id      create_time                month           
1	2020-09-29 11:59:01.326770    2020-09    2
2	2020-09-29 11:59:37.299591    2020-09


3	2020-10-29 09:59:53.029608    2020-10    4
4	2020-10-29 12:00:11.200812    2020-10
5	2020-10-29 12:00:26.995485    2020-10
10	2020-10-30 09:45:14.887788    2020-10

8	2020-08-01 09:03:16.000000    2020-08    2
9	2020-08-01 09:24:24.000000    2020-08

from django.db.models.functions import TruncMonth  # 需要导入该模块进行月切分

# filter:指定站点(用户)--> 划出年月的虚拟字段--> values:按年月分组--> 统计文章数 -->按月份倒序排列 -->输出月份与对应文章数
models.Article.objects.filter(blog=user.blog).annotate(month=TruncMonth('create_time'))
.values('month').annotate(c=Count('id')).order_by('-month').values_list('month', 'c')

页面样式

<div>
    <div class="panel panel-danger">
        <div class="panel-heading">
            <h3 class="panel-title">我的标签</h3>
        </div>
        <div class="panel-body">
            {% for tag in res_tag %}
                <p><a href="/{{ name }}/tag/{{ tag.2 }}.html">{{ tag.0 }}({{ tag.1 }})</a></p>
            {% endfor %}
        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔分类</h3>
        </div>
        <div class="panel-body">
            {% for category in res_category %}

                <p><a href="/{{ name }}/category/{{ category.2 }}.html">{{ category.0 }}({{ category.1 }})</a></p>
            {% endfor %}


        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔档案</h3>
        </div>
        <div class="panel-body">
            {% for month in res_month %}
                <p>
                	
                	<!--用过滤器只过滤出年月-->
                    <a href="/{{ name }}/archive/{{ month.0|date:'Y/m' }}.html">{{ month.0|date:'Y年m月' }}({{ month.1 }})</a>
                </p>
            {% endfor %}
        </div>
    </div>
</div>

二 侧边栏筛选(包含文章部分)

使点击某一站点标签、分类或随笔归档时,返回筛选出的所有文章

路由

# 三个有名分组,分别用来查询:用户名、筛选类别、
re_path('^(?P<name>\w+)/(?P<query>category|tag|archive)/(?P<condition>.*).html$', views.site),

页面

<div>
    <div class="panel panel-danger">
        <div class="panel-heading">
            <h3 class="panel-title">我的标签</h3>
        </div>
        <div class="panel-body">
            {% for tag in res_tag %}
                <p><a href="/{{ name }}/tag/{{ tag.2 }}.html">{{ tag.0 }}({{ tag.1 }})</a></p>
            {% endfor %}
        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔分类</h3>
        </div>
        <div class="panel-body">
            {% for category in res_category %}

                <p><a href="/{{ name }}/category/{{ category.2 }}.html">{{ category.0 }}({{ category.1 }})</a></p>
            {% endfor %}


        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔档案</h3>
        </div>
        <div class="panel-body">
            {% for month in res_month %}
                <p>
                    <a href="/{{ name }}/archive/{{ month.0|date:'Y/m' }}.html">{{ month.0|date:'Y年m月' }}({{ month.1 }})</a>
                </p>
            {% endfor %}
        </div>
    </div>
</div>

视图

from django.db.models import Count

def site(request, name, **kwargs):
    # 分三种情况(根据标签过滤,根据分类过滤,根据时间过滤)
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        article_list = user.blog.article_set.all()  # 拿出用户的所有文章
        # 根据不同情况对article_list进行过滤,article_list是queryset对象,可以继续filter
        query = kwargs.get('query', None)
        if query == 'category':  # 说明走的是过滤的路由
            condition = kwargs.get('condition')
            article_list = article_list.filter(category_id=condition)  # 从用户的所有文章中过滤出指定分类
        elif query == 'tag':
            condition = kwargs.get('condition')
            article_list = article_list.filter(tag__id=condition)  # 从用户所有文章中过滤出指定标签
        elif query == 'archive':
            year, month = kwargs.get('condition').split('/')  # 2020/09
            article_list = article_list.filter(create_time__year=year, create_time__month=month)  # 从用户所有文章中过滤出指定文章创建年月

# 如果上面三个判断都不成立当然就返回最初拿出的用户所有文章咯,也就是访问类似"www.IP号/用户名"这样用户主页时触发
		
		# 用来显示进入页面后,侧边栏的分类栏
        res_category = models.Category.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list(
            'name', 'num', 'id')
        
        # 用来显示进入页面后,侧边栏的标签栏
        res_tag = models.Tag.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list('name',
                                                                                                           'num', 'id')
        
        # 用来显示进入页面后,侧边栏的随笔归档                                                                                                   
        res_month = models.Article.objects.filter(blog=user.blog).annotate(month=TruncMonth('create_time')).values(
            'month').annotate(c=Count('id')).order_by('-month').values_list('month', 'c')

        return render(request, 'site.html', locals())
    else:
        return render(request, 'error.html')

三 侧边栏用inclusion_tag实现(不包含文章部分)

后端

不知各位还记不记得模板层学过的inclusion_tag,用途是可以生成一片模板中的代码块,详情请见Django第八日
我反正是不记得了

  1. 在app下创建一个包,templatetags,包的名字必须叫这个
  2. 在其中新建一个py文件,名字随意,这里我就给他命名my_tag.py
from django.db.models import Count
from django.template import library
from django.app名 import models

register = library.Library()

# 写一个函数,使用inclusion_tag装饰
@register.inclusion_tag('left.html')  # left.html是要导入的模板,是页面的一小部分
def left(username):  # 这个函数之后可以在页面中用模板语言调用
    user = models.UserInfo.objects.filter(username=username).first()
    res_category = models.Category.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list(
        'name', 'num', 'id')
    res_tag = models.Tag.objects.filter(blog=user.blog).annotate(num=Count('article__id')).values_list('name',
                                                                                                       'num', 'id')
    res_month = models.Article.objects.filter(blog=user.blog).annotate(month=TruncMonth('create_time')).values(
        'month').annotate(c=Count('id')).order_by('-month').values_list('month', 'c')

    return {'username':username,'res_category': res_category, 'res_tag': res_tag, 'res_month': res_month}

写要导入用的模板文件:left.html

<div class="这是侧边栏哦">
    <div class="panel panel-danger">
        <div class="panel-heading">
            <h3 class="panel-title">我的标签</h3>
        </div>
        <div class="panel-body">
            {% for tag in res_tag %}
                <p><a href="/{{ name }}/tag/{{ tag.2 }}.html">{{ tag.0 }}({{ tag.1 }})</a></p>
            {% endfor %}
        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔分类</h3>
        </div>
        <div class="panel-body">
            {% for category in res_category %}

                <p><a href="/{{ name }}/category/{{ category.2 }}.html">{{ category.0 }}({{ category.1 }})</a></p>
            {% endfor %}


        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">随笔档案</h3>
        </div>
        <div class="panel-body">
            {% for month in res_month %}
                <p>
                    <a href="/{{ name }}/archive/{{ month.0|date:'Y/m' }}.html">{{ month.0|date:'Y年m月' }}({{ month.1 }})</a>
                </p>
            {% endfor %}
        </div>
    </div>
</div>

收尾

  1. 可以将视图函数site.py里给侧边栏传参的三行代码删去了
  2. 将模板文件site.html中侧边栏标签里的三个小侧边栏删去,替换成如下代码
    {% load my_tag %}
    {% left username %}
    

四 使用上母版继承

因为文章详情页与文章筛选页只有文章部分在不断切换,筛选页中是一堆文章摘要,而详情页则是一整篇的文章,同时无论哪边标题、侧边栏都没有变化,所以可以将标题跟侧边栏放入母版

  1. 新建模板文件base.html,并在其中放入site.html文件的所有数据
  2. 清空’site.html’文件,并在其中写入盒子
    {% extend "base.html" %}
    {% block content %}
    {% endblock %}
    
    并将base.html里的文章div整个剪切到盒子中
  3. base.html中的文章div整个替换为
    <div class="col-md-10">
    {% block content %}
    {% endblock %}
    </div>
    
    注意不要连分区div也给删了,于是乎文章筛选页site.html完成了

  1. 下面开始写文章详情页,其实很简单,就是把整篇文章放入分区标签中:
    {% extends 'base.html' %}
    
    {% block title %}
        {{ article.title }}
    {% endblock %}
    
    {% block css %}
        <link rel="stylesheet" href="/static/css/mycss.css">
    {% endblock %}
    {% block content %}
        <div>
            <h3 class="text-center">{{ article.title }}</h3>
            <hr>
            <div>
                {{ article.content }}
            </div>
        </div>
    {% endblock %}
    
    re_path('^(?P<name>\w+)/article/(?P<id>\d+).html$', views.article_detail),
    
    def article_detail(request, name, id):
        user = models.UserInfo.objects.get(username=name)
        article = models.Article.objects.get(id=id)
        return render(request, 'article_detail.html', locals())
    

五 点赞点踩样式

html

<div id="div_digg">
    <div class="diggit action">
        <span class="diggnum" id="digg_count">{{ article.up_num }}</span>h
    </div>
    <div class="buryit action">
        <span class="burynum" id="bury_count">{{ article.down_num }}</span>
    </div>
    <div class="clear"></div>
    <div class="diggword" id="digg_tips" style="color: red;">
    </div>
</div>

css:从博客园抄来的,图片也是从那儿保存过来的,大家自由发fu挥zhi

/static/css中新建mycss.css

#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

.diggit {
    float: left;
    width: 46px;
    height: 52px;
    background: url(/static/img/up.gif) no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

.buryit {
    float: right;
    margin-left: 20px;
    width: 46px;
    height: 52px;
    background: url(/static/img/down.gif) no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

.clear {
    clear: both;
}

.diggword {
    margin-top: 5px;
    margin-left: 0;
    font-size: 12px;
    color: #808080;
}

六 点赞点踩功能完成

js

<script>
    $(".action").click(function () {
        var is_up = $(this).hasClass('diggit')
        var span = $(this).children('span')
        $.ajax({
            url: '/upanddown/',
            method: 'post',
            data: {
                article_id: '{{ article.id }}',
                is_up: is_up,
                csrfmiddlewaretoken: '{{ csrf_token }}'
            },
            success: function (data) {
                console.log(data)
                $('#digg_tips').html(data.msg)
                if (data.code == 100) {
                    //点赞或者点踩的数字加一
                    var num = Number(span.html()) + 1
                    span.html(num)
                }
            }
        })
    })
</script>

后端

def upanddown(request):
    res = {'code': 100, 'msg': ''}
    if request.user.is_authenticated:  # 如果登陆
        # article表中数字加1,在点赞点踩表中记录一条
        # 这个人对该文章只能点赞或者点踩一次
        # 先查一下,如果有记录了,就不能再点了
        article_id = request.POST.get('article_id')  # 获取文章id
        user_id = request.user.id  # 获取当前用户id
        is_up = request.POST.get('is_up')  # is_up是一个字符串
        print(type(is_up))  # 传过来的是字符串而不是布尔
        # if is_up=='true':  # 方式一:通过字符串校验
        #     is_up=True
        # else:
        #     is_up = False
        is_up=json.loads(is_up)  # 方式二:通过json将字符串转化成布尔
        print(type(is_up))
        res_1 = models.UpAndDown.objects.filter(article_id=article_id, user_id=user_id).count()  # 查询用户是否对该文章有操作记录
        if res_1:  # 有过
            res['code'] = 101
            res['msg'] = '已经点过了'
        else:  # 没有过
            with transaction.atomic():  # 事务
                models.UpAndDown.objects.create(article_id=article_id, user_id=user_id, is_up=is_up)  # 新增字段(操作记录,is_up为false时代表点的是踩)
                if is_up:
                    models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                    res['msg'] = '点赞成功'  # 准备给ajax的消息
                else:
                    models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                    res['msg'] = '点踩成功'  # 准备给ajax的消息

    else:  # 没有登陆
        res['code'] = 109
        res['msg'] = '请先<a href="/login/">登录</a>'  # 准备给ajax的消息
    return JsonResponse(res)  # 将消息发送给ajax

新增点赞点踩后的文章页面(将上面代码整合的完全体)

{% extends 'base.html' %}

{% block title %}
    {{ article.title }}
{% endblock %}

{% block css %}
    <link rel="stylesheet" href="/static/css/mycss.css">
{% endblock %}
{% block content %}
    <div>
        <h3 class="text-center">{{ article.title }}</h3>
        <hr>
        <div>
            {{ article.content }}
        </div>
        <!--
        点赞点踩
        -->
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article.up_num }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article.down_num }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;">
            </div>
        </div>
        <!--
        评论
        -->
        <div></div>
    </div>
{% endblock %}

{% block script %}
    <script>
    	// 点赞跟点踩按钮都有action类,所以无论点击哪个都会触发
        $(".action").click(function () {
        
        	// 当该按钮同时含有diggit类的时候赋值True
            var is_up = $(this).hasClass('diggit')

			// 将该按钮下的span子标签取出
            var span = $(this).children('span')
            $.ajax({
                url: '/upanddown/',
                method: 'post',
                data: {
                    article_id: '{{ article.id }}',  // 但概念文章id
                    is_up: is_up,  // 点赞时是True,点踩时是False
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (data) {  
                    console.log(data)
                    // 接收过来的文件data.msg,分很多情况,提示点过了、提示尚未登陆、提示点赞成功、提示点踩成功
                    $('#digg_tips').html(data.msg)  // 将提示信息渲染到页面上
                    if (data.code == 100) {
                        //点赞或者点踩的数字加一,因为var span赋值时就对应着当前点击的那个按钮,所以对应也会给那个按钮数字加一
                        var num = Number(span.html()) + 1  // 获取当前数字并加一
                        span.html(num)  // 将修改后的数字渲染顶替之前的数字
                    }
                }
            })
        })
    </script>
{% endblock %}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值