我们的博客侧边栏有四项内容:最新文章、归档、分类和标签云。这些内容相对比较固定,且在各个页面都会显示,如果像文章列表或者文章详情一样,从视图函数中获取然后传递给模板,则每个页面对应的视图函数里都要写一段获取这些内容的代码,这会导致很多重复代码。更好的解决方案是直接在模板中获取,为此,我们使用 Django 的一个新技术:自定义模板标签来完成任务。
使用模板标签的解决思路
我们前面已经接触过一些 Django 内置的模板标签,比如比较简单的 {% static %} 模板标签,这个标签帮助我们在模板中引入静态文件。还有比较复杂的如 {% for %} {% endfor%} 标签。这里 我们希望自己定义一个模板标签,例如名为 get_recent_posts
的模板标签,它可以这样工作:我们只要在模板中写入 {% get_recent_posts as recent_post_list %},那么模板中就会有一个从数据库获取的最新文章列表,并通过 as 语句保存到 recent_post_list
模板变量里。这样我们就可以通过 {% for %} {% endfor%} 模板标签来循环这个变量,显示最新文章列表了,这和我们在编写博客首页面视图函数是类似的。首页视图函数中从数据库获取文章列表并保存到 post_list
变量,然后把这个 post_list
变量传给模板,模板使用 for 模板标签循环这个文章列表变量,从而展示一篇篇文章。这里唯一的不同是我们从数据库获取文章列表的操作不是在视图函数中进行,而是在模板中通过自定义的 {% get_recent_posts %} 模板标签进行。
以上就是解决思路,但模板标签不是我们随意写的,必须遵循 Django 的规范我们才能在 Django 的模板系统中使用自定义的模板标签,下面我们就依照这些规范来实现我们的需求。
模板标签目录结构
首先在我们的 blog 应用下创建一个 templatetags 文件夹。然后在这个文件夹下创建一个 __init__.py 文件,使这个文件夹成为一个 Python 包,之后在 templatetags 目录下创建一个 blog_tags.py 文件,这个文件存放自定义的模板标签代码。
此时你的目录结构应该是这样的:
blog\
__init__.py
admin.py
apps.py
migrations\
__init__.py
models.py
static\
templatetags\
__init__.py
blog_tags.py
tests.py
views.py
编写模板标签代码
接下来就是编写各个模板标签的代码了,自定义模板标签代码写在 blog_tags.py 文件中。其实模板标签本质上就是一个 Python 函数,因此按照 Python 函数的思路来编写模板标签的代码就可以了,并没有任何新奇的东西或者需要新学习的知识在里面。
最新文章模板标签
打开 blog_tags.py 文件,开始写我们的最新文章模板标签。
blog/templatetags/blog_tags.py
from ..models import Post
def get_recent_posts(num=5):
return Post.objects.all().order_by('-created_time')[:num]
这个函数的功能是获取数据库中前 num
篇文章,这里 num
默认为 5。函数就这么简单,但目前它还只是一个纯 Python 函数,Django 在模板中还不知道该如何使用它。为了能够通过 {% get_recent_posts %} 的语法在模板中调用这个函数,必须按照 Django 的规定注册这个函数为模板标签,方法如下:
blog/templatetags/blog_tags.py
from django import template
from ..models import Post
register = template.Library()
@register.simple_tag
def get_recent_posts(num=5):
return Post.objects.all().order_by('-created_time')[:num]
这里我们首先导入 template 这个模块,然后实例化了一个 template.Library
类,并将函数 get_recent_posts
装饰为 register.simple_tag
。这样就可以在模板中使用语法 {% get_recent_posts %} 调用这个函数了。
注意 Django 1.9 后才支持 simple_tag 模板标签,如果你使用的 Django 版本小于 1.9,你将得到一个错误。Django 1.9 以前的版本如何自定义模板标签这里不再赘述。
归档模板标签
和最新文章模板标签一样,先写好函数,然后将函数注册为模板标签即可。
blog/templatetags/blog_tags.py
@register.simple_tag
def archives():
return Post.objects.dates('created_time', 'month', order='DESC')
这里 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 月这样一个时间列表,且降序排列,从而帮助我们实现按月归档的目的。
分类模板标签
过程还是一样,先写好函数,然后将函数注册为模板标签。注意分类模板标签函数中使用到了 Category
类,其定义在 blog.models.py 文件中,使用前记得先导入它,否则会报错。
blog/templatetags/blog_tags.py
from ..models import Post, Category
@register.simple_tag
def get_categories():
# 别忘了在顶部引入 Category 类
return Category.objects.all()
尽管侧边栏有 4 项内容(还有一个标签云),但是这里我们只实现最新文章、归档和分类数据的显示,还有一个标签云没有实现。因为标签云的实现稍有一点不同,所以将在接下来的教程中专门介绍。这里你也可以尝试着自己解决,如果遇到问题,可以通过官方文档或者搜索引擎求助。独立思考并寻求解决方案以及善用搜索引擎是一个开发者必须培养的能力,只有这样你才能成为一个独立的开发者,独立地解决别人可能从来没有遇到过的问题。
使用自定义的模板标签
打开 base.html,为了使用模板标签,我们首先需要在模板中导入存放这些模板标签的模块,这里是 blog_tags.py 模块。当时我们为了使用 static 模板标签时曾经导入过 {% load staticfiles %},这次在 {% load staticfiles %} 下再导入 blog_tags:
templates/base.html
{% load staticfiles %}
{% load blog_tags %}
<!DOCTYPE html>
<html>
...
</html>
然后找到最新文章列表处,把里面的列表修改一下:
templates/base.html
<div class="widget widget-recent-posts">
<h3 class="widget-title">最新文章</h3>
{% get_recent_posts as recent_post_list %}
<ul>
{% for post in recent_post_list %}
<li>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</li>
{% empty %}
暂无文章!
{% endfor %}
</ul>
</div>
这里我们通过使用 get_recent_posts
模板标签获取到最新文章列表,然后我们通过 as 语法(Django 模板系统的语法)将获取的文章列表保存进了 recent_post_list
模板变量中,之后就可以通过 for 循环来循环显示文章列表数据了,这和我们在写首页视图时是一样的。
然后是归档部分:
templates/base.html
<div class="widget widget-archives">
<h3 class="widget-title">归档</h3>
{% archives as date_list %}
<ul>
{% for date in date_list %}
<li>
<a href="#">{{ date.year }} 年 {{ date.month }} 月</a>
</li>
{% empty %}
暂无归档!
{% endfor %}
</ul>
</div>
同样,这里我们调用 archives
模板标签自动获取一个已发表文章的日期列表,精确到月份,降序排列,然后通过 as 语法将其保存在 date_list
模板变量里。由于日期列表中的元素为 Python 的 date
对象,因此可以通过其 year
和 month
属性分别获取年和月的信息,<a href="#">{{ date.year }} 年 {{ date.month }} 月</a>
反应了这个事实。
分类部分也一样:
<div class="widget widget-category">
<h3 class="widget-title">分类</h3>
{% get_categories as category_list %}
<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>
显示的是该分类下的文章数目,这个特性会在接下来的教程中讲解如何实现,目前暂时用占位数据代替吧。
现在运行开发服务器,可以看到侧边栏显示的数据已经不再是之前的占位数据,而是我们保存在数据库中的数据了。
注意:如果你按照教程的步骤做完后发现报错,请按以下顺序检查。
- 检查目录结构是否正确。确保 templatetags 位于 blog 目录下,且目录名必须为 templatetags。具体请对照上文给出的目录结构。
- 确保 templatetags 目录下有 __init__.py 文件。
- 确保使用的 Django 版本不小于 1.9。
- 确保通过
register = template.Library()
和@register.simple_tag
装饰器将函数装饰为一个模板标签。 - 确保在使用模板标签以前导入了 blog_tags,即 {% load blog_tags %}。注意要在使用任何 blog_tags 下的模板标签以前导入它。
- 确保模板标签的语法使用正确,即 {% load blog_tags %},注意 { 和 % 以及 % 和 } 之间没有任何空格。
总结
本章节的代码位于:Step10: side bar。
如果遇到问题,请通过下面的方式寻求帮助。
- 在下方评论区留言。
- 将问题的详细描述通过邮件发送到 djangostudyteam@163.com,一般会在 24 小时内回复。
- 在 Pythonzhcn 社区的新手问答版块 发布帖子。
-- EOF --
为什么这边一定要as后才能显示正常,内容不为空
同问
as 将获取结果存入后面指定的模板变量,这是django模板标签的用法。
我的是一直在报错,不是{ % get categories as category_list % }这句话里面get 没有找到,就是{ % empty % }里面的empty找不到,没有定义什么的,稍微改正一下%和{的距离就好了,可能就是编译器一时间反应不过来{}和%之间的关系,识别不出,所以导致一直报错。希望可以给其他人一些帮助。。。
为啥我的models没有objects的属性了,我在blog_tags.py里导入Post和Category的models后,在函数里返回Category.objects.all()的时候提示没有objects的属性,在models.py文件里也一样,但是我在shell里面却可以,有没有大佬知道是什么原因呀。提示如下:
Inspection info: This inspection detects names that should resolve but don't. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Top-level and class-level items are supported better than instance items.
这个提示是pycharm里面的
在models.py文件中显示地加入一个管理器就行了即objects = models.ModelManager()就行了
@python_2_unicode_compatible 你的python是3.0+吧,加上这个装饰器就好了
也可以用自定义中间件的方法实现获取数据吧
请问这个在写的时候是有指定在浏览器中的位置吗,为什么这些内容就是在侧边栏,而不是在正中间什么的呢
我在遇到django.template.exceptions.TemplateSyntaxError: 'blog_tags' is not a registered tag library.问题时
在settings.py文件中加入了'libraries'就ok了。
。。我也是这个报错,就是不知道怎么加setting,学习了
又在下面找到说不用修改setting,
ctrl+c,重新python manage.py runserver就好了
感觉还是把模板注册进去更好些吧
这是什么原理?setting里的键值是不区分大小写的吗?
应该是区分大小写的吧,就是把你自己写的模板标签注册到你的模板库里的意思。我是这么认为的,希望可以帮助到你。
我和你碰到的问题一样,后来发现是我的代码出问题了。我是这么解决的
blog_tags.py中
register = template.Library() #而我写成register = template.library()
确保使用的 Django 版本不小于 1.9。
我用的1.11.8的版本 按照步骤没有出错
我看到有些同学出现了
的错误,我出现这个错误的原因是我的应用名是myblog,并没有用老师的blog,然后我改名了之后用{% load myblog_tags %}就可以了
Django 2.0版本不需要
写上反而会出错
2.0.3写上没错
操作报错
ValueError at /Cannot truncate TimeField 'created_time' to DateField. Request Method:GETRequest URL:http://localhost:8000/Django Version:2.0.1Exception Type:ValueErrorException Value:Cannot truncate TimeField 'created_time' to DateField. Exception Location:C:\Users\Keyones\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\functions\datetime.py in resolve_expression, line 193Python Executable:C:\Users\Keyones\AppData\Local\Programs\Python\Python36\python.exePython Version:3.6.3Python Path:['E:\\kesites', 'E:\\kesites',
使用 DatetimeField 代替 TimeField
TemplateSyntaxError at /Invalid block tag on line 86: 'empty'. Did you forget to register or load this tag?
请问为什么会出现empty Tag报错,没有用自定义 Tag之前没有报错啊。
请问你解决了吗?我也碰到了这个问题
我也遇到了这个问题,应该在如何加这个empty
各位使用Templatetags没有生效的朋友,可以参考下这个https://stackoverflow.com/questions/40686201/django-1-10-1-my-templatetag-is-not-a-registered-tag-library-must-be-one-of
需要重启Django的服务器。
stackoverflow的这个链接第二个有效,在settings的.py的templates中做自定义标签的引入
您好 可以写一下自定义标签引入在本教程里的代码吗 我看了那个但是一直都试不对= =新手入门,请您多多指教!
对于@register.simple_tag这里
我在django 1.8.2环境中要用@resister.assignment_tag 赋值标签,才能 as 给其他变量.否则会报参数过多错误,有跟我一样问题的吗
版本兼容性问题,你的方法是对的。
为什么侧边栏显示暂无文章啊
<div class="widget widget-recent-posts"> <h3 class="widget-title">最新文章</h3> {% get_recent_posts as recent_post_list %} <ul> {% for post in recent_post_list %} <li> <a href="{{ post.get_absolute_url }}">{{ post.title }}</a> </li> {% empty %} 暂无文章! {% endfor %} </ul> </div>
我重新复制了一下博主的代码,可以了
我也是重新复制,不过我觉得这样好尴尬。
恩,不知道为什么,一样的代码
你们代码不好用的时候是手打的么还是说复制上面去的?
没有查到数值吧
博主,你好!为什么我的侧边栏都显示“暂无文章!”“暂无归档!”“暂无分类!”?
想问一下啊,你解决了嘛,我也遇到了这个问题
没有
博主请教个问题,这个模板标签,归档部分有没有好的办法同时获得记录数?例如:2017-08(10条) 这样
参考文档 annotate 以及 aggregate 部分,不过我没有细致研究过。
博主大大 按照你的步骤操作了一遍,页面上没出现侧边栏,也没报错。。。。什么情况啊?
我知道了。。
重启服务没用,重启电脑就可以了。。。
博主前辈:
InvalidTemplateLibrary at /
Invalid template library specified. ImportError raised when trying to load 'blog.templatetags.blog_tags': cannot import name 'templete'
为什么 cannot import name 'templete' ??
啊········我把template 写成了 templete!!!!
博主,
from ..models import Post, Category
报错如下:
from ..models import Post, Category
SystemError: Parent module '' not loaded, cannot perform relative import
请问怎么解决呢?目录结构是对的
请问下你这个问题解决了么,我也遇到了同样问题
如果你是右边栏里的分类没有信息的话
那么你应该检查下你get_recent_posts里的方法里是否return了
def get_recent_post():
return Post.objects.all().order_by('-created_time')
单独去运行python blog_tags.py是会报这个错误的 我也没有解决
但是对于项目来说没有影响貌似 只要跟着上面教程做然后记得有返回值就可以返回数据库中的数据列表了
py3么?templatetags 的目录结构是不是和项目中的一样?
博主 我也是重启之后没有反应
django.template.exceptions.TemplateSyntaxError: 'blog_tags' is not a registered tag library. Must be one of:
admin_list
admin_modify
admin_static
admin_urls
cache
i18n
l10n
log
static
staticfiles
tz
需要在settings里面加上STATIC_URL = '/templatetags/'
可是我打开setting的时候发现已经有
STATIC_URL = '/static/'
继续加上去的话,不会覆盖嘛
见评论末尾,重启服务即可解决,无需修改settings
TemplateSyntaxError at /
'./blog_tags' is not a valid tag library: Template library ./blog_tags not found, tried django.templatetags../blog_tags,django.contrib.admin.templatetags../blog_tags,django.contrib.staticfiles.templatetags../blog_tags
请问这是怎么回事,反复试了好几次都有问题
重启一下服务器看看
好像markdown不能支持蓝字链接语法?
支持呀,是不是格式不正确?
复制我自己在简书的文章过来,只有不行...
您好,实际上应该是结果可以插入点开链接,但是字是黑色的不会变成蓝色,我误以为没有用,和其他文本颜色一样的话确实也很难知道可以点击
额,这个主题貌似就是这样,可以自己改改css链接颜色。
谢谢~
谢谢
我遇到了个问题:
'created_time' is a DateTimeField, not a DateField.
暂时的处理方法是:
在Django项目的settings.py文件中,可以直接设置为“USE_TZ = False”
你需要传入完整的 datetime 对象。
return Post.objects.dates('created_time', 'month', order='DESC')
这里dates改成datetimes,接着报错缺少pytz。安装pytz之后页面正常
我的提示安装后我安装了还是报错,重启服务器也不行啊
请问这个最后怎么解决?
TemplateSyntaxError at /
Invalid block tag on line 80: 'get_recent_posts'. Did you forget to register or load this tag?
-----------这边的base.xml部分内容为: 是有什么没有定义?
最新文章
{% get_recent_posts as recent_post_list %}{% for post in recent_post_list %}-
{{ post.title }}
{% empty %}
暂无文章!
{% endfor %}
你重启一下服务器试试。
请问你的问题解决了吗? 我也是相同的问题,找了一天都没有解决方法,希望你能给给回复,谢谢
先试一下重启 这个问题我忘记记录了 忘记当时是怎么处理完成的 有可能是模板有问题 你那边可以试一下替换博主的本教程的模板内容看一下
请问一下,这段代码如果放在django1.8版本上该怎样修改?另外,我看django-1.8的手册上有simple_tag,为什么说django1.9以上才支持呢?
1.9 之后才支持 as 语法,1.9以前你可以把 simple tag 改为 assignment tag
原来如此,谢谢博主。
add:需要重启服务器,才会生效。否则,报错:'blog_tags' is not a registered tag library. Must be one of: admin_list admin_modify admin_static admin_urls cache i18n l10n log static staticfiles tz
对,需要重启。
重启服务器后还是报错,请问可能会是什么情况?
'blog_tags' is not a registered tag library. Must be one of: admin_list admin_modify admin_static admin_urls blog_tag cache i18n l10n log static staticfiles tz
看看你的项目结构。
博客不方便粘贴代码的话请发在 http://pythonzh.cn/category/django/
最近在期末考试,考完了之后我能直接在QQ上问您吗?
嗯,可以的
我也是同样的问题,重启不惯用,版本1.11
'blog_tags' is not a registered tag library. Must be one of:
admin_list
admin_modify
admin_static
admin_urls
cache
i18n
l10n
log
static
staticfiles
tz
按照教程中的检查提示做了还是没用么?
在base.html中引用的{% load blog_tags %}需要指明blog_tags的路径,在setting.py里面加上STATIC_URL = '/templatetags/'即可
blog_tags 是模板标签,这种做法是错误的。只要 blog_tags 所在的 app 已经加入了 INSTALLED_APPS 列表,django 应该就能导入这个模板标签的。
这种情况主要重启一下本地服务器就好了。不需要更改 STATIC_URL = '/static/'
重启和修改static都试过了,都不管用
已解决,tempatetags放错目录了。
重启服务器也不一定有用,可能突然就好了。也不知道是什么原因
浏览器缓存...