11. 页面侧边栏

作者: 追梦人物 | 来源:发表于2017-04-19 18:28 被阅读359次

本教程内容已过时,更新版教程请访问: Django 博客开发入门教程

这是 Django 博客教程的第 11 篇,在阅读此篇教程以前,请确保你已阅读 Django 博客教程的前 10 篇:
1. Django 博客教程:前言
2. 搭建开发环境
3. 建立我们的 django 博客应用
4. 创建 django 博客的数据库模型
5. 让 django 完成翻译——迁移数据库模型
6. django 博客首页视图
7. 真正的 django 博客首页视图
8. 在 django admin 后台发布我们的文章
9. 博客文章详情页
10. 支持 markdown 语法和代码高亮

我们的博客侧边栏有三项内容:最近发表的博客文章、归档和分类。这些内容相对比较固定,且在各个页面都会显示,如果像文章列表或者文章详情一样,在视图函数中获取然后传递给模板,则每个页面对应的视图函数里都要写一段获取这些内容的代码,然后传递给模板,这会导致很多重复代码。更好的解决方案是直接在模板中获取,为此,我们使用 django 的一个新技术:自定义模板标签来完成我们的任务。

我们接触过一些 django 内置的模板标签,比如比较简单的 {% static %} 模板标签,这个标签帮助我们在模板中引入静态文件。还有比较复杂的如 {% for %} {% endfor%} 标签。这里我们希望自己定义一个模板标签,它可以这样工作:我们只要在模板中写入 {% get_recent_posts as recent_post_list %},那么模板中就会有一个从数据库获取的最近文章列表,并通过 as 语句保存到 recent_post_list 模板变量里,这样我们就可以通过 for 模板标签来循环这个变量,显示文章列表了。这和我们在编写博客首页面视图函数是一样的,它在视图函数中从数据库获取文章列表输入保存到 post_list 变量,并把这个 post_list 变量传给模板,模板使用 for 模板标签循环这个文章列表变量,从而展示一条条文章列表。这里唯一的不同是我们从数据库获取文章列表的操作不是在视图函数中进行,而是在模板中通过我们定义的 get_recent_posts 模板标签进行。

以上就是解决思路,但模板标签不是我们随便乱写的,必须遵循 django 的规范我们才能在模板语言使用我们定义的模板标签,下面我们就依照这些规范来实现我们的需求。

首先在我们的 blog 应用下创建一个 templatetags 文件夹。然后在这个文件夹下创建一个 __init__.py 文件,使这个文件夹变为一个 Python 包,之后在创建一个 blog_tags.py 文件,这个文件存放我们自定义的模板标签的代码。

打开 blog_tags.py 文件,开始写我们的模板标签。模板标签本质上就是一个 Python 函数,比如这里我们的 {% get_recent_posts %},它应该对应着一个函数,这个函数将从数据库获取最近文章列表并返回他们。

templatetags/blog_tags.py

from ..models import Post

def get_recent_posts(num=5):
    return Post.objects.all()[:num]

这个函数的功能是获取数据库中前 num 篇文章,这里 num 默认为 5。函数就这么简单,但目前它还只是一个纯 Python 函数,django 模板还不知道该如何使用它。为了能够通过 {% get_recent_posts %} 在模板中调用这个函数,必须按照 django 的规定注册这个函数为模板标签,方法如下:

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()[:num]

这里我们首先导入 template 这个包,然后实例化了一个 template.Library,并将函数装饰为 register.simple_tag。这样就可以在模板中使用语法 {% get_recent_posts %} 调用这个函数了。

如法炮制,实现归档,分类列表。

归档:

templatetags/blog_tags.py

@register.simple_tag
def archives():
    return Post.objects.dates('created_time', 'month', order='DESC')

这个 dates 方法会返回一个列表,列表中的元素为每一篇 Post 创建的时间,精确到月份,降序排列。接受的三个参数值表明了这些含义,一个是 created_time ,即 Post 的创建时间,month 是精度,order='DESC' 表明降序排列。例如如果我们写了 3 篇文章,分别发布于 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28 日,那么 date 将返回 2017 年 3 月 和 2017 年 2 月这样一个时间列表,从而帮助我们实现按月归档的目的。

分类:

templatetags/blog_tags.py

@register.simple_tag
def get_categories():
    return Category.objects.all()

然后在模板中使用它们:

打开 base.html,为了使用模板标签,我们首先需要在模板中导入它。当时我们为了使用 static 模板标签时曾经导入过 {% load staticfiles %},这次先在最顶部导入 blog_tags:

templates/base.html

{% load blog_tags %}
<!DOCTYPE html>
<html>
...
</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>
        {% endfor %}
    </ul>
</div>

这里我们通过使用 get_recent_posts 模板标签获取到最新文章列表,然后我们通过 as 将获取的文章列表保存进了 recent_post_list 模板变量中,之后就可以通过 for 循环来循环显示了,这和我们在写首页视图时是一样的。

然后是归档部分:

<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>
    {% endfor %}
  </ul>
</div>

同样,这里我们调用 archives 模板标签获取自动获取一个文章发表的日期列表,精确到月份,然后保存在 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 }}</a>
    </li>
    {% endfor %}
  </ul>
</div>

相关文章

网友评论

  • yushengzhou:{% get_recent_posts as recent_post_list %} 为什么非得使用 这个语句啊 我不使用就不显示最近文章下面的内容 我直接使用的{% for article in get_recent_posts %}就不显示
    追梦人物:@yushengzhou 先要用过 as 语句保存到模板变量,这是 Django 的语法规则,没办法。
  • 童话小当家:出现错误templateSyntaxError at / '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 无法解决的时候尝试重启服务器
  • 24d2dc7efacd:你好,请问一下为什么我按照步骤来弄了但是页面侧边栏还是和之前一样都是英文和固定的时间之类的
    追梦人物:@水哥并不水 欢迎加入django学习小组,群号码:561422498。详细描述一下问题,仅凭这些无法定位问题
    24d2dc7efacd:@追梦人物 有的,就是打开base.html修改了里面的<div class="widget widget-recent-posts">
    <h3 class="widget-title">最新文章</h3>..........
    ....等等各个部分的内容,而且最顶部也加了{% load blog_tags %}
    追梦人物:@水哥并不水 有修改模板显示的内容么
  • yushengzhou::clap: 第十一期 O(∩_∩)O哈哈~
    e936bd950684:from django import template
    from ..models import Post,Category
    register = template.Library()


    @register.simple_tag
    def get_recent_posts(num=2):
    return Post.objects.all()[:num]

    @register.simple_tag
    def archives():
    return Post.objects.dates('created_time', 'month', order='DESC')

    @register.simple_tag
    def get_categories():
    return Category.objects.all()
    追梦人物:@辣条辣条5毛一包 传多了参数,把模板标签的代码贴出来看下
    e936bd950684:这一节你做了么
    为什么我这显示这个错误
    Error during template rendering

    In template C:\Django Web\2017.4.17\blogproject\blog\templates\blog\base.html, error at line 75
    'get_recent_posts' received too many positional arguments

本文标题:11. 页面侧边栏

本文链接:https://www.haomeiwen.com/subject/enuhzttx.html