likes
comments
collection
share

Django 的基础模板和模板文件重构

作者站长头像
站长
· 阅读数 18

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 30 天,点击查看活动详情

前言

在上一篇文章《Django 博客文章查看与静态文件设置》中,我们成功的将静态文件设置好。

bootstrap-4.3.1 下的 cssjs 文件导入,也已经简单的使用 bootstrap.min.css 将我们得博客变得好看一点,不过细心的读者发现,我们使用的 home.htmlcontent.html 两个文件中包含重复的 HTML 代码,有没有好的方式改变一下这种情况?

基础模板

为了展示主页和详细的博客文章信息,我们会复制和粘贴 HTML 文档的多个部分(比如头部信息),其实,Django 为我们提供了很好的模板继承功能,只要我们写好基础模板,然后将静态文件放在基础模板中使用,其他模板添加你自己需要定制的部分。就能大量减少我们的代码量,接下来看看怎么做:

首先在 ./templates 文件夹中创建一个名为 base.html 的文件:

{% load static %}<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Django 博客{% endblock %}</title>
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    </head>
    <body>

    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-expand" href="{% url 'home' %}">Django 博客</a>
        </div>
    </nav>
        <div class="container">
            <ol class="breadcrumb my-4">
                {% block breadcrumb %}
                {% endblock %}
            </ol>
            {% block content %}
            {% endblock %}
        </div>
    </body>
</html>

这是我们的母模板,之后我们创建的模板都 继承 自这个特殊的模板。现在我们介绍 {% block %} 标签。它用于在模板中保留一个空间,一个“子”模板(继承这个母模板)可以在这个部分中插入代码和 HTML。

Django 提供了强大的模板语言用于控制数据渲染,由模板标签(template tags)模板变量(templates variables)模板过滤器(templates filters 组成:

  • templates tags:进行渲染控制,类似 {% tag %}
  • templates variables:可认为是模板标签的一种特殊形式,即只是一个变量,渲染的时候只替换内容,类似 {{ variable }}
  • templates filters:附加在模板变量上改变变量最终显示结果,类似 {{ variable|filter }}

{% block title %}Django 博客{% endblock %} 中,设置了一个默认值“Django 博客”,如果子模板没有设置 {%block title%} 的值,它就会被使用。

接下来,我们改写我们之前写过的两个模板:home.htmlcontent.html

./templates/home.html

{% extends 'base.html' %}
{% block breadcrumb %}
    <li class="breadcrumb-item active" >
        <a href="https://blog.csdn.net/yuzhou_1shu">欢迎来到宇宙之一粟的技术漂泊之旅</a>
    </li>
{% endblock %}
{% block content %}
    <table class="table">
        <thead class="thead-dark">
            <tr>
                <th>标题</th>
                <th>作者</th>
                <th>发布时间</th>
            </tr>
        </thead>
        <tbody>
            {% for blog in blogs %}
                <tr>
                    <td>
                        <a href="{{ blog.id }}">{{ blog.title }} </a>
                    </td>
                    <td>
                        {{ blog.author }}
                    </td>
                    <td>
                        {{ blog.publish }}
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

home.html 的第一行是 {%extend 'base.html'%}。这个标签用来告诉 Django 使用 base.html 作为母模板,使用 block 来放置这个页面独有的部分。

./templates/content.html

{% extends 'base.html' %}
{% block title %}
    {{ article.title }} - {{ article.author.username }}
{% endblock %}

{% block breadcrumb %}
    <li class="breadcrumb-item"><a href="{% url 'home' %}">Home</a></li>
    <li class="breadcrumb-item active">{{ article.author.username }}</li>
{% endblock %}

{% block content %}
    <div class="blog-post" >
        <h2 class="blog-post-title">{{ article.title }}</h2>
        <p class="blog-post-meta">
           Published {{ article.publish }} by
            <a href="#"> {{ article.author.username }}</a>
        </p>
    <p>
        {{ article.body|truncatewords:30|linebreaks }}
    </p>
    </div>
{% endblock %}

content.html 中,我们改变了{%block title%} 的默认值,然后运行整个 Django,访问到主页,如下图:

Django 的基础模板和模板文件重构

随便选择并点击进入一篇博客,查看博客详细信息,如下图:

Django 的基础模板和模板文件重构

总结

我们能看到两个界面都有了导航页,说明母模板里面的内容被 home.htmlcontent.html 继承下来,并各自能展示自己想要显示的内容。

其实,如果你还想分的更细的话,一般网站可以分为上、中、下三个部分。上部就是网站的头部,可以放导航栏,网站图标或商标,所以可以自己设置头部基础模板 header.html;中部就是你要向访问者展示的内容,比如博客、列表、图片,这个就是子模板该做的内容;下部就是网站底部,可以显示网站的版权信息或者友情链接等一般不怎么改变的内容,所以自己可以定义一个 footer.html

其实,关于模板,作者的审美也不是很好,所以网页布局也不太好看,感觉目前这个博客有点四不像,不过没关系,我们边写边学,可以参考网上好看的博客网站(偷偷学一下别人的优秀设计)。

主页改版

之前主页是由表格显示的,对于博客网站不是很合理,在参考很多博客设计之后,想着对主页进行改版。改完之后的 home 页面显示如下: Django 的基础模板和模板文件重构

修改 base.html

在导航页增加了一个登录选项,为下一章做铺垫。

{% load staticfiles %}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}{% endblock %}</title>
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    </head>
    <body>
        <div id="header">
            <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
                <div class="container" >
                    <a class="navbar-expand" href="{% url 'home' %}">主页</a>
                    <ul class="nav navbar-nav navbar-light" style="margin-right: 10px">
                        <li><a href="#">登录</a> </li>
                    </ul>
                </div>
            </nav>
        </div>
        <div class="container">
            <div id="content">
                {% block content %}
                {% endblock %}
            </div>
        </div>
    </body>
</html>

修改 home.html

{% extends 'base.html' %}
{% block title %} 主页 {% endblock %}

{% block content %}
        <h1>博客首页</h1>
        {% for blog in blogs %}
            <h2>
                <a href="{{ blog.id }}">{{ blog.title }} </a>
            </h2>
            <p class="date">
            Posted by {{ blog.author.username }} on {{ blog.publish }}
            </p>
            {{ blog.body|truncatewords:30|linebreaks }}
        {% endfor %}
{% endblock %}

解释:truncatewords 用来截断指定数量的文字,linebreaks 将结果带上一个 HTML 换行。

添加分页功能

当输入一些文章后,就会需要将文章分页进行显示,Django 自带了一个分页器。 编辑 blog/views.py 文件,修改 home 函数:

from django.shortcuts import render, get_object_or_404
from .models import BlogArticles
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

# Create your views here.
def home(request):
    object_list = BlogArticles.objects.all()
    paginator = Paginator(object_list, 5)
    page = request.GET.get('page')
    try:
        blogs = paginator.page(page)
    except PageNotAnInteger:
        # 如果page参数不是一个整数就返回第一页
        blogs = paginator.page(1)
    except EmptyPage:
        # 如果页数超过总页数就返回最后一页
        blogs = paginator.page(paginator.num_pages)
    return render(request, "home.html", {"page": page, "blogs": blogs})

def blog_article(request, article_id):
    # article = BlogArticles.objects.get(id=article_id)
    article = get_object_or_404(BlogArticles, id=article_id)
    publish_time = article.publish
    return render(request, "content.html", {"article": article, "publish": publish_time})

分页器相关代码解释如下:

  1. 使用要分页的内容和每页要展示的内容数量,实例化 Paginator 类得到 paginator 对象
  2. 通过 get() 方法获取 page 变量,表示当前的页码
  3. 调用 paginator.page() 方法获取要展示的数据
  4. 如果 page 参数不是一个整数就返回第一页,如果页数超过总页数就返回最后一页
  5. 把页码和要展示的内容传给页面。

为分页功能创建一个单独的模板,可以让该模版用在任何使用分页功能的页面中,在 blog/templates/ 目录中新建一个 pagination.html,编写如下代码:

<div class="pagination">
    <span class="step-links">
        {% if page.has_previous %}
            <a href="?page={{ page.previous_page_number }}">前一页</a>
        {% endif %}

    <span class="current">
        Page {{ page.number }} of {{ page.paginator.num_pages }}.
    </span>
    {% if page.has_next %}
        <a href="?page={{ page.next_page_number }}">后一页</a>
    {% endif %}
    </span>
</div>

然后回到 blog/templates/home.html 文字,添加分页功能实现完毕:

{% extends 'base.html' %}
{% block title %} 主页 {% endblock %}

{% block content %}
        <h1>博客首页</h1>
        {% for blog in blogs %}
            <h2>
                <a href="{{ blog.id }}">{{ blog.title }} </a>
            </h2>
            <p class="date">
            Posted by {{ blog.author.username }} on {{ blog.publish }}
            </p>
            {{ blog.body|truncatewords:30|linebreaks }}
        {% endfor %}
        {% include 'pagination.html' with page=blogs %}        # 新增
{% endblock %}

总结

本文针对模板文件中的主页和博客内容模板重复代码部分进行了重构,将重复模板抽离出来写成母模板。然后给文章中的博客主页添加了文章分页功能,可以看到 Django 提供了丰富的接口方便开发者。

最后,让我们来回顾一下这些基本的 Django 知识点,这一部分关于 Django 的基础知识点就介绍到这,如果你还想深入研究并使用 Django 进行开发,建议回顾和学习一下之前的几篇文章,还有一定要多多查看官方文档。

Django 的基础模板和模板文件重构

参考资料:

  1. 《Django by Example》
  2. 《Django 入门与实践》
  3. Django Project

希望本文能对你有所帮助,如果喜欢本文,可以点个关注。

这里是宇宙之一粟,下一篇文章见!

宇宙古今无有穷期,一生不过须臾,当思奋争。