#第 12 篇:解锁博客侧栏,GoGoGo!

作者:HelloGitHub-追梦人物
文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库
我们的博客侧边栏有四项内容:最新文章、归档、分类和标签云。这些内容相对比较固定和独立,且在各个页面都会显示,如果像文章列表或者文章详情一样,从视图函数中获取这些数据然后传递给模板,则每个页面对应的视图函数里都要写一段获取这些内容的代码,这会导致很多重复代码。更好的解决方案是直接在模板中获取,为此,我们使用 django 的一个新技术:自定义模板标签来完成任务。
使用模板标签的解决思路
我们前面已经接触过一些 django 内置的模板标签,比如比较简单的 {% static %} 模板标签,这个标签帮助我们在模板中引入静态文件。还有比较复杂的如 {% for %} {% endfor%} 标签。这里我们希望自己定义一个模板标签,例如名为 show_recent_posts 的模板标签,它可以这样工作:我们只要在模板中写入 {% show_recent_posts %},那么模板中就会渲染一个最新文章列表页面,这和我们在编写博客首页面视图函数是类似的。首页视图函数中从数据库获取文章列表并保存到 post_list 变量,然后把这个 post_list 变量传给模板,模板使用 for 模板标签循环这个文章列表变量,从而展示一篇篇文章。这里唯一的不同是我们从数据库获取文章列表的操作不是在视图函数中进行,而是在模板中通过自定义的 {% show_recent_posts %} 模板标签进行。
以上就是解决思路,但模板标签不是随意写的,必须遵循 django 的规范才能在 django 的模板系统中使用,下面就依照这些规范来实现我们的需求。
模板标签目录结构
首先在我们的 blog 应用下创建一个 templatetags 文件夹。然后在这个文件夹下创建一个 __init__.py 文件,使这个文件夹成为一个 Python 包,之后在 templatetags\ 目录下创建一个 blog_extras.py 文件,这个文件存放自定义的模板标签代码。
此时你的目录结构应该是这样的:
blog\
__init__.py
admin.py
apps.py
migrations\
__init__.py
models.py
static\
templatetags\
__init__.py
blog_extras.py
tests.py
views.py
编写模板标签代码
接下来就是编写各个模板标签的代码了,自定义模板标签代码写在 blog_extras.py 文件中。其实模板标签本质上就是一个 Python 函数,因此按照 Python 函数的思路来编写模板标签的代码就可以了,并没有任何新奇的东西或者需要新学习的知识在里面。
最新文章模板标签
打开 blog_extras.py 文件,开始写我们的最新文章模板标签。
from django import template
from ..models import Post, Category, Tag
register = template.Library()
@register.inclusion_tag('blog/inclusions/_recent_posts.html', takes_context=True)
def show_recent_posts(context, num=5):
return {
'recent_post_list': Post.objects.all().order_by('-created_time')[:num],
}
这里我们首先导入 template 这个模块,然后实例化了一个 template.Library 类,并将函数 show_recent_posts 装饰为 register.inclusion_tag,这样就告诉 django,这个函数是我们自定义的一个类型为 inclusion_tag 的模板标签。
inclusion_tag 模板标签和视图函数的功能类似,它返回一个字典值,字典中的值将作为模板变量,传入由 inclusion_tag 装饰器第一个参数指定的模板。当我们在模板中通过 {% show_recent_posts %}使用自己定义的模板标签时,django 会将指定模板的内容使用模板标签返回的模板变量渲染后替换。
inclusion_tag 装饰器的参数 takes_context 设置为 True 时将告诉 django,在渲染 _recent_posts.html 模板时,不仅传入show_recent_posts 返回的模板变量,同时会传入父模板(即使用 {% show_recent_posts %} 模板标签的模板)上下文(可以简单理解为渲染父模板的视图函数传入父模板的模板变量以及 django 自己传入的模板变量)。当然这里并没有用到这个上下文,这里只是做个简单演示,如果需要用到,就可以在模板标签函数的定义中使用 context 变量引用这个上下文。
接下来就是定义模板 _recent_posts.html 的内容。在 templates\blogs 目录下创建一个 inclusions 文件夹,然后创建一个 _recent_posts.html 文件,内容如下:
<div class="widget widget-recent-posts">
<h3 class="widget-title">最新文章</h3>
<ul>
{% for post in recent_post_list %}
<li>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</li>
{% empty %}
暂无文章!
{% endfor %}
</ul>
</div>
很简单,循环由 show_recent_posts 传递的模板变量 recent_post_list 即可,和 index.html 中循环显示文章列表是一样的。
归档模板标签
和最新文章模板标签一样,先写好函数,然后将函数注册为模板标签即可。
@register.inclusion_tag('blog/inclusions/_archives.html', takes_context=True)
def show_archives(context):
return {
'date_list': Post.objects.dates('created_time', 'month', order='DESC'),
}
这里 Post.objects.dates 方法会返回一个列表,列表中的元素为每一篇文章(Post)的创建时间(已去重),且是 Python 的 date 对象,精确到月份,降序排列。接受的三个参数值表明了这些含义,一个是 created_time ,即 Post 的创建时间,month 是精度,order='DESC' 表明降序排列(即离当前越近的时间越排在前面)。例如我们写了 3 篇文章,分别发布于 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28 日,那么 dates 函数将返回 2017 年 3 月 和 2017 年 2 月这样一个时间列表,且降序排列,从而帮助我们实现按月归档的目的。
然后是渲染的模板 _archives.html 的内容:
<div class="widget widget-archives">
<h3 class="widget-title">归档</h3>
<ul>
{% for date in date_list %}
<li>
<a href="#">{{ date.year }} 年 {{ date.month }} 月</a>
</li>
{% empty %}
暂无归档!
{% endfor %}
</ul>
</div>
由于 date_list 中的每个元素都是 Python 的 date 对象,所以可以引用 year 和 month 属性来获取年份和月份。
分类模板标签
过程还是一样,先写好函数,然后将函数注册为模板标签。注意分类模板标签函数中使用到了 Category 类,其定义在 blog.models.py 文件中,使用前记得先导入它,否则会报错。
@register.inclusion_tag('blog/inclusions/_categories.html', takes_context=True)
def show_categories(context):
return {
'category_list': Category.objects.all(),
}
_categories.html 的内容:
<div class="widget widget-category">
<h3 class="widget-title">分类</h3>
<ul>
{% for category in category_list %}
<li>
<a href="#">{{ category.name }} <span class="post-count">(13)</span></a>
</li>
{% empty %}
暂无分类!
{% endfor %}
</ul>
</div>
<span class="post-count">(13)</span> 显示的是该分类下的文章数目,这个特性会在接下来的教程中讲解如何实现,目前暂时用占位数据代替吧。
标签云模板标签
标签和分类其实是很类似的,模板标签:
@register.inclusion_tag('blog/inclusions/_tags.html', takes_context=True)
def show_tags(context):
return {
'tag_list': Tag.objects.all(),
}
_tags.html:
<div class="widget widget-tag-cloud">
<h3 class="widget-title">标签云</h3>
<ul>
{% for tag in tag_list %}
<li>
<a href="#">{{ tag.name }}</a>
</li>
{% empty %}
暂无标签!
{% endfor %}
</ul>
</div>
使用自定义的模板标签
打开 base.html,为了使用刚才定义的模板标签,我们首先需要在模板中导入存放这些模板标签的模块,这里是 blog_extras.py 模块。当时我们为了使用 static 模板标签时曾经导入过 {% load static %},这次在 {% load static %} 下再导入 blog_extras:
templates/base.html
{% load static %}
{% load blog_extras %}
<!DOCTYPE html>
<html>
...
</html>
然后找到侧边栏各项,将他们都替换成对应的模板标签:
templates/base.html
<aside class="col-md-4">
{% block toc %}
{% endblock toc %}
{% show_recent_posts %}
{% show_archives %}
{% show_categories %}
{% show_tags %}
<div class="rss">
<a href=""><span class="ion-social-rss-outline"></span> RSS 订阅</a>
</div>
</aside>
此前侧边栏中各个功能块都替换成了模板标签,其实实际内容还是一样的,只是我们将其挪到了模块化的模板中,并有这些自定义的模板标签负责渲染这些内容。
此外我们定义的 show_recent_posts 标签可以接收参数,默认为 5,即显示 5 篇文章,如果要控制其显示 10 篇文章,可以使用 {% show_recent_posts 10 %} 这种方式传入参数。
现在运行开发服务器,可以看到侧边栏显示的数据已经不再是之前的占位数据,而是我们保存在数据库中的数据了。
注意:
如果你是在开发服务器启动的过程中编写的模板标签代码,那么一定要重启一下开发服务器才能导入 blog_extras,否则会报
TemplateSyntaxError at /
'blog_extras' is not a registered tag library. Must be one of:
类似这样的错误。
注意:如果你按照教程的步骤做完后发现报错,请按以下顺序检查。
- 检查目录结构是否正确。确保 templatetags\ 位于 blog\ 目录下,且目录名必须为 templatetags。具体请对照上文给出的目录结构。
- 确保 templatetags\ 目录下有 __init__.py 文件。
- 确保通过
register = template.Library()和@register.inclusion_tag装饰器将函数装饰为一个模板标签。 - 确保在使用模板标签以前导入了 blog_extras,即 {% load blog_extras%}。注意要在使用任何 blog_extras下的模板标签以前导入它。
- 确保模板标签的语法使用正确,即 {% load blog_extras %},注意 { 和 % 以及 % 和 } 之间没有任何空格。

欢迎关注 HelloGitHub 公众号,获取更多开源项目的资料和内容
#第 12 篇:解锁博客侧栏,GoGoGo!的更多相关文章
- [python][django学习篇][15]博客侧栏--自定义模板标签
我们的博客侧边栏有四项内容:最新文章.归档.分类和标签云. 这些内容相对比较固定,且在各个页面都会显示,如果像文章列表或者文章详情一样,从视图函数中获取然后传递给模板,则每个页面对应的视图函数里都要写 ...
- 使用django开发博客过程记录3——博客侧栏实现
说起这个侧栏真是苦恼我很长时间,一开始以为和之前的一样传递额外参数就可以了就像下面这样: class IndexView(ListView): template_name = 'apps/index. ...
- 我的第一篇Markdown博客
我的第一篇Markdown博客 这是我第一次用Markdown写博客,发现还是比较好用的,加上Marsedit也支持了Markdown的博客预览,博客园也加了Markdown的格式支持,就更加方便了, ...
- Socket网络编程--FTP客户端(60篇socket博客,而且都比较简单、深入浅出)
已经好久没有写过博客进行分享了.具体原因,在以后说. 这几天在了解FTP协议,准备任务是写一个FTP客户端程序.直接上干货了. 0.了解FTP作用 就是一个提供一个文件的共享协议. 1.了解FTP协议 ...
- 人生中的第一篇OI博客及博客规划
这是笔者第一次在博客园里发表文章,也同样是第一次来写关于OI的一些想法,此篇的主题是想总体对日后的博客有具体的规划. 首先,笔者创办博客并发表观点于看法的目的是记录自己对于题目或竞赛的观念,主要以题解 ...
- Ceph相关博客、网站(256篇OpenStack博客)
官网文档: http://docs.ceph.com/docs/master/cephfs/ http://docs.ceph.com/docs/master/cephfs/createfs/ ( ...
- PHP採集CSDN博客边栏的阅读排行
项目中要用到採集的数据,所以就先拿CSDN博客来试了试.这里使用Simple HTML DOM(官网)这个库,它可以方便的遍历HTML文档. <?php include_once('simple ...
- 五、Abp vNext 基础篇丨博客聚合功能
介绍 业务篇章先从客户端开始写,另外补充一下我给项目起名的时候没多想起的太随意了,结果后面有些地方命名冲突了需要通过手动using不过问题不大. 开工 应用层 根据第三章分层架构里面讲到的现在我们模型 ...
- 第一篇CSDN博客,大家好!
大家好,我是负雪明烛! 我这昵称的来源是喜欢一句很有意蕴的古诗--苍山负雪,明烛天南. 我喜欢这句诗,很多的账号都用了这个"负雪明烛"的昵称,如果大家在其他地方看到叫这个名字的人, ...
随机推荐
- CF1195C Basketball Exercise (dp + 贪心)
题解出处:https://www.luogu.org/problemnew/solution/CF1195C 很水的一道C题……目测难度在黄~绿左右.请各位切题者合理评分. 注意到可以选择的球员编号是 ...
- Vue的基本使用(一)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- sqlserver2014创建数据库时,错误提示如下:尝试打开或创建物理‘c:\数据库\db.mdf’时,Create File遇到操作系统错误5(拒绝访问)
CREATE DATABASE test1 ON PRIMARY ( NAME =test1, FILENAME='C:\Program Files\test1.mdf',SIZE=10240K ...
- Java 之MVC动态分页完美实现
一个分页小技术有时也是让人挠头,在这里完全前端实现方式与Java的实现方式,我们提供给你完全的编码参考,希望能够帮到你哦(:) 内容导读 1.程序结构 2.JSP页面设计 3.分页主要编码 4.运行效 ...
- 2019上半年总结——Github上那些Java面试、学习相关仓库
分享一下最近逛Github看到了一些对于Java面试以及学习有帮助的仓库,这些仓库涉及Java核心知识点整理.Java常见面试题.算法.基础知识点比如网络和操作系统等等. 知识点相关 1.JavaGu ...
- Java----面向对象(继承&多态)
一.继承 什么是继承 ? 让类与类之间产生了子父类关系 ; 继承的好处是: 提高代码的复用性和维护性 java中继承的特点是: 只支持单继承.不支持多继承,但是可以多层继承; 四种权限修饰符是 : p ...
- vue通信、传值的多种方式(详细)
转载自https://blog.csdn.net/qq_35430000/article/details/79291287
- Unity经典游戏教程之:雪人兄弟
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Cannot attach the file “MvcMovie.mdf” as database “aspnet-MvcMovie”
今天在微软开发人员官网上学习asp.net mvc5入门的时候,遇到一个棘手的问题,我是按照教程一步一步操作的,但期间遇到一个自己觉得莫名其妙的问题,教程中也没有提到这个, 在添加新字段这一章节,跟着 ...
- 伪分布式Spark + Hive on Spark搭建
Spark大数据平台有使用一段时间了,但大部分都是用于实验而搭建起来用的,搭建过Spark完全分布式,也搭建过用于测试的伪分布式.现在是写一遍随笔,记录一下曾经搭建过的环境,免得以后自己忘记了.也给和 ...