template标签_Django实战: 利用自定义模板标签实现仿CSDN博客月度归档

应网友慕之岩的请求,现提供下Django项目中如何使用自定义标签实现仿CSDN博客的月度归档(如下图所示)。要求按月统计每个月发表的博文篇数, 跳过空白月份,最后结果按发布时间逆序排列。点击每个月份可以看到详细博文列表清单。本文着重讲述如何实现,不包括样式美化。如代码手机上看不全,可以搜索知乎大江狗或csdn大江狗。

09a89d7a2338f76dc893464439b2af2f.png

实现原理

假如我们有如下一个Article模型(完整的见Django实战专题: 开发专业博客(1)之内容管理后台开发),里面包含了status和发布日期pub_date两个关键字段。

class Article(models.Model):"""文章模型"""STATUS_CHOICES = (        ('d', '草稿'),('p', '发表'),)    title = models.CharField('标题', max_length=200, db_index=True)    slug = models.SlugField('slug', max_length=60, blank=True)    body = RichTextUploadingField('正文')    pub_date = models.DateTimeField('发布时间', null=True)    create_date = models.DateTimeField('创建时间', auto_now_add=True)    mod_date = models.DateTimeField('修改时间', auto_now=True)    status = models.CharField('文章状态', max_length=1, choices=STATUS_CHOICES, default='p')

我们实现原理如下:

  • 我们需要构建一个由年份、月份和和当月发表文章数量组成的字典列表。新的列表由如{'year': 2019, 'month': 8, total:'4'}这样的多个字典组成。

  • 我们先获取所有已发表文章的最大年份和最小年份,缩小归档时间范围。

  • 利用for循环查询最大年份和最小年份间每年的12个月中是否有文章发表(逆序查找),如果有,我们将统计该月发表文章的数量,并将由年份、月份和和文章数量构建成的字典插入列表。如果当月无文章发表,我们直接跳过。

  • 最后我们在模板中循环输出每条记录的year, month和total即可。同时我们还需要为每条记录建个url,用于展示对应year和month的月度文章详细列表。

自定义模板标签

我们将自定义一个{% show_monthly_archive %}的标签,用于显示月度归档。这样做的好处是显而易见的,因为如果你将来想在不同页面比如文章详情、文章列表或搜索页面显示月度归档信息时,你只需要在相应页面模板加入这么一行代码就可以了,而不需要更改视图增加新的context,也不需要对模板进行大的调整。

更多关于自定义模板标签的内容见Django基础(16): 模板标签(tags)的分类及如何自定义模板标签。本文只做简要介绍。首先你要在你的app目录下新建一个叫templatetags的文件夹(不能取其它名字), 里面必需包含__init__.py的空文件。在该目录下你还要新建一个python文件专门存放你自定义的模板标签函数,本例中为blog_extras.py,当然你也可以取其它名字。整个目录结构如下所示:

blog/
  __init__.py
  models.py
  templatetags/
      __init__.py
      blog_extras.py
  views.py

在模板中使用自定义的模板标签时,需要先使用{% load blog_extras %}载入自定义的过滤器,然后通过{% show_monthly_archive %} 使用它。

在blog_extras.py中添加如下代码。该自定义标签的作用创建字典列表,  并通过包含的模板_monthly_archive_list.html循环输出列表结果。因为这个模板并不是用于显示整个页面的主模板,而只是些模板片段,所以我们在它前面加了下划线_。

#blog_extras.py

from django import templatefrom ..models import Articleregister = template.Library()# show rendered template@register.inclusion_tag('blog/_monthly_archive_list.html')def show_monthly_archive():#按日期逆序排序articles = Article.objects.filter(status='p').order_by('-pub_date')#获取最大和最小年份max_year = articles[0].pub_date.year    min_year = articles[len(articles)-1].pub_date.year#按年和月循环,排除空月份,生成子一个字典列表rows = []for year in range(max_year, min_year -1, -1):for month in range(12, 0, -1):            total = Article.objects.filter(pub_date__year=year,pub_date__month=month).count()if total > 0:                rows.append({"year": year, "month": month, "total": total, })else:continue    return {'rows': rows, }

模板blog/_monthly_archive_list.html代码如下:

归档{% for row in rows %}href="{% url 'blog:month_archive' row.year row.month %}">{{ row.year }}年{{ row.month }}月 {{ row.total }}篇{% endfor %}

视图views.py和urls.py

事情还没结束。我们还需要自定义一个名为month_archive的视图及其对应urls来根据年份year和月份month来显示文章清单。注意这里的month_archive.html是主模板哦,我们还加入了分页。

#urls.py

path('articles///', views.month_archive, name='month_archive'),

#views.py

def month_archive(request, year, month):    articles = Article.objects.filter(status='p', pub_date__year=year,pub_date__month=month).order_by('-pub_date')    paginator = Paginator(articles, 3)    page = request.GET.get('page')    page_obj = paginator.get_page(page)    context = {'page_obj': page_obj, 'paginator': paginator, 'is_paginated': True}return render(request, 'blog/month_archive.html', context)

#blog/month_archive.html

{% extends "blog/base.html" %}{% block content %}

月度归档{# 注释: page_obj不要改。Article可以改成自己对象 #}{% if page_obj %}class="table table-striped">标题发布日期查看
{% for article in page_obj %}{{ article.title }}{{ article.pub_date | date:"Y-m-d" }} href="{% url 'blog:article_detail' article.id article.slug %}">class="glyphicon glyphicon-eye-open">
{% endfor %}{% else %}{# 注释: 这里可以换成自己的对象 #}没有文章。{% endif %}{# 注释: 下面代码一点也不要动, #}{% if is_paginated %}
class="pagination">{% if page_obj.has_previous %}class="page-item">class="page-link" href="?page={{ page_obj.previous_page_number }}">«{% else %}class="page-item disabled">class="page-link">«{% endif %} {% for i in paginator.page_range %} {% if page_obj.number == i %}class="page-item active">class="page-link"> {{ i }} class="sr-only">(current){% else %}class="page-item">class="page-link" href="?page={{ i }}">{{ i }}{% endif %} {% endfor %} {% if page_obj.has_next %}class="page-item">class="page-link" href="?page={{ page_obj.next_page_number }}">»{% else %}class="page-item disabled">class="page-link">»{% endif %}{% endif %}{% endblock %}

查看效果

我们们在article_detail.html加入{% load blog_extras %}和 {% show_monthly_archive %}, 这时的文章详情页面就包含月度归档信息了。点击链接可以查看每月文章列表。

249988eb24d31e953761ba2e5adef040.png

最后的话

统计哪些月份是否表了文章以及每个月发表了多少篇文章是非常耗时的运算。如果用户每访问一个页面,都需要重新计算一次会造成很大的资源浪费。这部分信息其实更新的并不是那么快,完全可以使用缓存把计算结果缓存起来,如下所示。更多内容见Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用。

#在模板中使用cache

{% load cache %}

{% cache 500 %}

{% show_monthly_archive %}

{% endcache %}

你有更好的实现办法吗? 如果你有什么功能或业务需求需要实现的,欢迎留言。小编我会根据个人兴趣有选择性地提供解决方案。

大江狗

2019.09.21

378527b3d7b9b519f01c1e588918ae5f.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javascript的stack overflow

写一个很简单的页面和脚本 1<html>2<head>3<title>fanweixiaos test</title>4<script type"text/javascript">5function onclick() 6{ 7 alert(范维肖); 8} 9</script>10</head>11<body>12<input type"b…

Hibernate中inverse属性与cascade属性

Hibernate集合映射中&#xff0c;经常会使用到"inverse"和"cascade"这两个属性。对于我这样&#xff0c;Hibernate接触不深和语文水平够烂的种种因素&#xff0c;发现这两个属性实在是难以理解&#xff0c;无奈只好将这个两个属性解释工作交给了Google和Ba…

h5 nan_h5页面在不同ios设备上的问题总结

最近在写嵌入到小程序webview的一个h5页面&#xff0c;是一个文章评论的功能&#xff0c;这个过程中&#xff0c;遇到很多兼容性的问题&#xff0c;在不同机型上的表现也很不一致&#xff0c;所以总结了以下这些问题&#xff0c;记录下来&#xff0c;以便以后查看。1、日期问题…

C# 淘宝商品微信返利助手开发-(七)返利助手开发(5)如何将优惠券地址转为淘口令

系列教程一目录&#xff1a;返利助手原理 系列教程二目录&#xff1a;返利助手开放文档以及帐号申请地址 系列教程三目录&#xff1a;返利助手开发&#xff08;1&#xff09;API介绍 系列教程四目录&#xff1a;返利助手开发&#xff08;2&#xff09;淘宝分享的内容如何只取…

ftp改为sftp_科普!一文详解 FTP、FTPS 与 SFTP 的原理

FTP、FTPS 与 SFTP 简介FTPFTP 即 文件传输协议&#xff08;英语&#xff1a;File Transfer Protocol 的缩写&#xff09;是一个用于计算机网络上在客户端和服务器之间进行文件传输的应用层协议。完整的 FTP 是由 FTP 服务器 和 FTP 客户端组成的&#xff0c;客户端可以将本地的…

动手学servlet(四) cookie和session

Cookie cookie是保存在客户端的一个“键值对”&#xff0c;用来存储用户的一些信息 cookie的应用&#xff1a; -在电子商务会话中标识用户 -对网站进行定制&#xff0c;比如你经常浏览哪些内容&#xff0c;就展示哪些页面给你 -网站广告&#xff0c;比如百度联盟&#xff0c;你…

16进制 转为图片 php_Python 十六进制hex-bytes-str之间的转换和Bcc码的生成

前言近期做测试模拟器用到了hex-bytes-str之间的转换bcc码的校验&#xff0c;这里总结了一些方法。实例直接上代码转为十六进制&#xff08;Hex&#xff09;字符串def 执行代码&#xff1a;方法&#xff1a;getStringFromNumber(size,value)参数一为生成几个batys&#xff0c;参…

推荐一个国外SaaS产品-Olark

Olark www.olark.com 是国外非常出名的一个在线客服工具。 现在国内很多人创业失败的原因是&#xff1a;什么都想自己做&#xff0c;不利用现有资源&#xff0c;结果造成做出来的东西什么都有&#xff0c;什么都不好用。 我们应该好好学习国外的小团队&#xff0c;他们只做一件…

粗暴,干就完了----徐晓冬似的C语言自学笔记-----实现一个链表结构

1 #include <stdio.h>2 #include <stdlib.h>3 #define N 54 /*N 假定数组长度为5*/5 typedef struct snode6 {7 int data;8 struct snode *next; 9 } SNODE;10 11 /*第一步&#xff0c;添加链表头信息*/12 SNODE *createhead(int a[])13 {14 SNODE *h,…

C# 淘宝商品微信返利助手开发-(二)返利助手开放文档以及帐号申请地址

系列教程一目录&#xff1a;返利助手原理 系列教程二目录&#xff1a;返利助手开放文档以及帐号申请地址 系列教程三目录&#xff1a;返利助手开发&#xff08;1&#xff09;API介绍 系列教程四目录&#xff1a;返利助手开发&#xff08;2&#xff09;淘宝分享的内容如何只取…

比较难的sql面试题,令我比较郁闷!

一组通话记录&#xff08;总共500万条&#xff09;:ID 主叫号码 被叫号码 通话起始时间 通话结束时间 通话时长1 98290000 0215466546656 2007-02-01 09:49:53.000 2007-02-01 09:50:16.000 232 98290000 021546654666 2007-02-01 09:50:29.000 2007-02-01 09:5…

阿里云ESC服务器数据快速转移至轻量应用服务器

ECS服务器的换ECS的服务器已有的数据转换就很简单了&#xff0c;直接创建自定义镜像&#xff0c;创建完成后新服务器直接更换系统盘然后选择你创建的镜像就直接吧你的数据可软件一起直接移到新的ECS的服务器了 可是坑爹的是&#xff0c;新买的轻量应用服务器里面左找右找也找…

js udp通信_nodejs源码分析第十九章 -- udp模块

udp不是面向连接的协议&#xff0c;所以使用上会比tcp简单&#xff0c;他和tcp一样&#xff0c;使用四元组来标记通信的双方&#xff08;单播的情况下&#xff09;。我们看看udp作为服务器和客户端的时候的流程。1 在c语言中使用udp1.1 服务器流程&#xff08;伪代码&#xff0…

SVN 清理失败解决方案

SVN有时因各种不明原因导致清理失败&#xff0c;可以采取如下解决办法进行处理&#xff1a; 方法一&#xff1a; 删除根目录下隐藏文件夹“.svn” 然后在根目录文件夹 外面的空白处 检出。比如你项目文件夹名为“D:/source” 则svn检出时,在“source”外面的D盘(D:/) 空白处上右…

将SQL-SERVER逆向工程导入Power-Design中并给表的字段添加注释

PD是一款不错的数据库设计工具&#xff0c;我们在项目开发的时候直接采用正向工程&#xff0c;设计好数据库后逆向将数据库导入PD中&#xff0c;并在PD中添加数据库字段的注释&#xff0c;便于新人的理解和学习&#xff0c;PD支持Oracle、SqlServer等数据库&#xff0c;是很强大…

腾讯微博Android客户端开发——自动获取验证码

上一节给大家讲解通过调用android系统自带的浏览器进行授权认证的&#xff0c;使用该种方式能很容易的完成认证&#xff0c;但是该种方式有个弊端&#xff0c;也就是如果使用第三方的浏览器如UC、天天等&#xff0c;输入完QQ账号信息点击“授权”后并不能再次跳转到MainActivit…

put请求方式参数如何传_TP5请求(request)变量

可以通过Request对象完成全局输入变量的检测、获取和安全过滤&#xff0c;支持包括$_GET、$_POST、$_REQUEST、$_SERVER、$_SESSION、$_COOKIE、$_ENV等系统变量&#xff0c;以及文件上传信息。检测变量是否设置可以使用has方法来检测一个变量参数是否设置&#xff0c;如下&…

python numpy的var std cov研究

var&#xff1a;表示方差&#xff0c; 即各项-均值的平方求和后再除以N &#xff0c; std&#xff1a;表示标准差&#xff0c;是var的平方根。 cov&#xff1a;协方差 ,与var类似&#xff0c;但是除以(N-1) import numpy as np# 构建测试数据&#xff0c;均值为10 sc [9.7, 10…

Vue手动封装实现一个五星评价得效果

我是歌谣 放弃很难 但是坚持一定很酷 微信公众号关注小歌谣 一起学习前后端知识 今天要说得是实现一个vue中实现五星评价得效果 简单来说 就是封装组件把 具体需要我们了解组件间得相互传值 数据绑定等知识 先用脚手架起个项目先 脚手架启动 ​ 安装依赖 包括 npm ins…