学习Django的模板语言系统

188 阅读8分钟

Django模板是带有额外语法的HTML文件,允许使用变量、循环和其他控制结构。当一个视图调用render()函数时,它将数据传入模板,而模板则生成HTML显示给用户。这在许多MVC和MVT Web框架中是一个非常类似的概念。Django为自己的模板系统使用了一个内置的后端,它被称为Django模板语言或DTL。因为这是Django默认的模板系统,所以我们将在这个Django模板教程中学习这个系统。

Django模板语法

Django模板的语法有三个部分。它们是变量、标签和带有过滤器变量

  • **{{ variable }}**当变量名称在双大括号内使用时,会显示变量的值。这是一种插值的形式。
  • **{% tag %}**模板标签被置于带百分号的大括号内,用于循环、if else结构、结构元素以及一些控制逻辑。
  • **{{ variable|filter }}**在Django模板中,一个变量后面也可以有一个管道字符来使用一个模板过滤器。模板过滤器将一个字符串作为输入,并返回一个字符串作为输出。

模板过滤器的工作方式类似于shell脚本中的管道。过滤器用于数据格式化,如将文本设置为标题或大写字母。

模板变量实例

作为一个在模板中使用变量的例子,我们可以看一下我们在上一个教程中创建的dog_detail.html模板。根据我们访问的URL,不同的狗被传入模板。这里有一个h2标签,我们用双括号来呈现狗的名字属性。结果是h2标签内的字符串Winnie。
djangodogs/templates/dog_detail.html

<h2>{{ dog.name }}</h2>

结果

<h2>Winnie</h2>

如何在Django模板中循环处理数据

如果你有几个需要显示的项目,你就需要有在集合上循环显示每个项目的能力。这通常是在主页模板中的情况。在我们的例子中,我们有几个狗,我们想在访问主页时显示。在这个例子中,我们有一个狗实例的查询集,在变量 **dogs**变量中查询狗的实例,主页视图将其传递到模板中。我们使用 **for**模板标签来循环处理每个 **dog**实例。请注意,这段话末尾的 **endfor**标签来标记循环的结束,这是必要的。如果它被遗漏了,你会得到一个错误。在这个循环中,我们可以使用 **dog**变量,在这里,我们用 **dog.name**在一个h1标签中渲染狗的名字。结果是为数据库中的每只狗都有一个h1标签,每个标签都显示它们的名字。

djangodogs/templates/home.html

<body>
{% for dog in dogs %}
    <h1>{{ dog.name }}</h1>
{% endfor %}
</body>

结果

<h1>Winnie</h1>

<h1>Mosely</h1>

<h1>Bella</h1>

URL模板标签

有些模板标签没有相应的结束标签,只是呈现一个字符串,而 **url**标签就是这样的一个例子。这个标签将url-pattern的名称作为参数,并返回该模式的路径。这就是url-pattern的name参数变得有用的地方。在下面的代码中,无论给定的ID是什么,url标签都会渲染到狗的详细视图的路径。因此,根据所提供的ID,url可能翻译为/dogs/1/、/dogs/2/、/dogs/3/,等等。你可能会问,为什么要使用url标签,而不是直接为主视图和狗的详细链接硬编码href。url标签所提供的是一种对未来的保护。通过使用url标签而不是硬编码,我们可以决定以后改变url模式,我们在模板中使用的链接仍然可以工作。

<body>
{% for dog in dogs %}
    <a href="{% url 'dog_detail' dog.id %}">
        <h1>{{ dog.name|capfirst }}</h1>
    </a>
{% endfor %}
</body>

结果

<body>

    <a href="/dogs/1/">
        <h1>Winnie</h1>
    </a>

    <a href="/dogs/2/">
        <h1>Mosely</h1>
    </a>

    <a href="/dogs/3/">
        <h1>Bella</h1>
    </a>

</body>

模板过滤器

模板过滤器允许对传入模板的变量进行输出格式化,以便在页面上呈现。现在在我们的案例中,我们已经在数据库中添加了一些狗,第一个字母是大写的。所以当我们输出这些狗时,我们看到它们的名字有一个大写字母,如上图所示。模板过滤器只是管道字符后的一个字符串。我们可以用小写模板过滤器使狗的名字全部变成小写。下面是一个例子。

<body>
{% for dog in dogs %}
    <a href="{% url 'dog_detail' dog.id %}">
        <h1>{{ dog.name|lower }}</h1>
    </a>
{% endfor %}
</body>
</body>

结果

<body>

    <a href="/dogs/1/">
        <h1>winnie</h1>
    </a>

    <a href="/dogs/2/">
        <h1>mosely</h1>
    </a>

    <a href="/dogs/3/">
        <h1>bella</h1>
    </a>

</body>

让我们再试一下模板过滤器的例子。这次我们将使用大写过滤器,它应该把我们的变量格式化为全部大写。

<body>
{% for dog in dogs %}
    <a href="{% url 'dog_detail' dog.id %}">
        <h1>{{ dog.name|upper }}</h1>
    </a>
{% endfor %}
</body>
</body>

结果

<body>

    <a href="/dogs/1/">
        <h1>WINNIE</h1>
    </a>

    <a href="/dogs/2/">
        <h1>MOSELY</h1>
    </a>

    <a href="/dogs/3/">
        <h1>BELLA</h1>
    </a>

</body>

在Django中,有很多模板过滤器可用。其中一些包括add, addslashes, capfirst, center, cut, date, default, default_if_none, dictsort, dictsortreversed, divisibleby, escape, escapejs, filesizeformat, first, floatformat, force_escape, get_digit, iriencode, join, json_script, last, length, length_is, linebreaks, linebreaksbr, linenumbers, ljust,lower, make_list, phone2numeric, pluralize, pprint, random, rjust, safe, safeseq, slice, slugify, stringformat, striptags, time, timesince, timeuntil, title, truncatechars, truncatechars_html, truncatewords, truncatewords_html, unordered_list, upper, urlencode, urlize, urlizetrunc, wordcount, wordwrap, and yesno.要了解所有这些如何工作,你可以查看Django模板标签和过滤器参考。

Django模板的继承性

模板继承是Django模板的一项功能,它使用了 **extends**和 **block**标签。在Django中,我们可以使用一个基础模板,其中包含每个模板都会使用的元素,比如元标签、任何全局CSS或JavaScript,或者其他我们不想在每个模板中都重新声明的结构元素,如果你没有必要的话,重复代码是不好的。在这个例子中,我们在名为base.html的模板文件中定义了一些HTML模板。在它的body标签中,我们有一个block模板标签,为子模板提供一个定义其独特内容的地方。

base.html

<!DOCTYPE html>
<html>
  <head>
    <!-- meta tags, etc.. -->
  </head>
  <body>
    {% block content %} 
    
    {% endblock content %}
  </body>
</html>

现在我们可以调整主模板,利用 **extends**标签,以base.html为参数。这样就可以将主模板配置为使用base.html模板作为其父模板。延伸声明必须是模板的第一行。这就是我们如何确保这个模板中的任何块状标签将被用来表示一个内容区域,它将覆盖和填充父模板的部分。在这个例子中,我们正在覆盖名为content的块。当渲染时,主页模板将有来自基础模板的HTML,而它的body标签将被放置在content块中的任何内容所填充。下面是它的实际效果。

home.html

{% extends 'base.html' %}

{% block content %}
    {% for dog in dogs %}
        <a href="{% url 'dog_detail' dog.id %}">
            <h1>{{ dog.name }}</h1>
        </a>
    {% endfor %}
{% endblock content %}

结果

<!DOCTYPE html>
<html>
  <head>
    <!-- meta tags, etc.. -->
  </head>
  <body>
    
        <a href="/dogs/1/">
            <h1>Winnie</h1>
        </a>
    
        <a href="/dogs/2/">
            <h1>Mosely</h1>
        </a>
    
        <a href="/dogs/3/">
            <h1>Bella</h1>
        </a>
    
  </body>
</html>

在Django模板中使用Bootstrap

现在我们知道了如何实现一个可以包含任何重复的模板代码的base.html模板,让我们给我们的例子添加一些Bootstrap的样式,使它们看起来比原始的Html好一点。首先,我们可以在base.html文件中添加bootstrap的启动模板标记。注意blockendblock标签,它们显示了动态内容将被插入的位置。

base.html

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
          integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</title>
</head>
<body class="container mt-4">
{% block content %}

{% endblock content %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
        integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
        integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
        crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
        integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
        crossorigin="anonymous"></script>
</body>
</html>

现在在我们的home.html文件中,我们可以用这个 **extends**标签来继承base.html文件中的所有标记。由于该文件有指向Bootstrap CSS CDN的链接,我们可以在home.html模板的Html元素上使用Bootstrap类。

home.html

{% extends 'base.html' %}

{% block content %}
    {% for dog in dogs %}
        <div class="card mb-4">
            <div class="card-body">
                <h4 class="card-title">{{ dog.name }}</h4>
                <h6 class="card-subtitle mb-2 text-muted">Dog number {{ dog.id }}</h6>
                <p class="card-text">{{ dog.name }} is a wonderful dog!</p>
                <a href="{% url 'dog_detail' dog.id %}" class="btn btn-primary">Learn more about {{ dog.name }}</a>
            </div>
        </div>
    {% endfor %}
{% endblock content %}

bootstrap django template

我们还可以更新dog_detail.html模板,以便在查看单个狗的时候,造型看起来不错。

dog_detail.html

{% extends 'base.html' %}

{% block content %}
    <div class="jumbotron">
        <h1 class="display-4">Hello, {{ dog.name }}!</h1>
        <p class="lead">The breed of this dog is {{ dog.breed }}</p>
        <p>Description: {{ dog.description }}</p>
    </div>
{% endblock content %}

django template detail view

如何添加静态文件

在这一点上,我们已经有了一个体面的小狗网站,但我们还没有谈到如何添加我们自己的CSS、图片或JavaScript文件。这些都是在网络项目领域中被称为静态文件的东西。在任何基于网络的应用程序中,你将需要包括静态文件。因此,为了在我们的Django项目中包含静态文件,我们可以在项目的根目录中添加一个新的文件夹,名为static。在这个文件夹里面,我们可以根据需要创建更多的文件夹。在大多数情况下,你会有一个文件夹用来存放图片,一个用来存放CSS文件,还有一个用来存放任何要包含的JavaScript文件。

配置STATICFILES_DIRS

静态文件可以在每个应用程序中配置,也可以在每个项目中配置。在我们的案例中,我们将看到如何将静态文件添加到项目中。这样,如果你愿意,这些静态文件可以在不同的应用程序中共享。为了配置这个,我们进入djangotutorial/settings.py并添加以下代码。

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

有了上述设置,我们就可以轻松地访问静态目录中的文件和文件夹了。

django static files for templates

{% load static %}

现在,让我们调整所有其他模板文件所继承的base.html文件。我们可以删除之前的CDN链接,并把对静态文件的引用放在原位。

base.html

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    {% load static %}
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">

    <link rel="stylesheet" href="{% static 'css/main.css' %}">

    <title>Hello, world!</title>
</head>
<body class="container mt-4">
{% block content %}

{% endblock content %}
</body>
</html>

home.html
在home.html文件中,我们也使用static关键字来加载一个图片。

{% extends 'base.html' %}

{% block content %}
    {% load static %}
    <img class="img-custom" src="{% static "images/dog.png" %}" alt="My image">
    {% for dog in dogs %}
        <div class="card mb-4">
            <div class="card-body">
                <h4 class="card-title">{{ dog.name }}</h4>
                <h6 class="card-subtitle mb-2 text-muted">Dog number {{ dog.id }}</h6>
                <p class="card-text">{{ dog.name }} is a wonderful dog!</p>
                <a href="{% url 'dog_detail' dog.id %}" class="btn btn-primary">Learn more about {{ dog.name }}</a>
            </div>
        </div>
    {% endfor %}
{% endblock content %}

结果

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="/static/css/bootstrap.min.css">

    <link rel="stylesheet" href="/static/css/main.css">

    <title>Hello, world!</title>
</head>
<body class="container mt-4">
 
    <img class="img-custom" src="/static/images/dog.png" alt="My image">
    
        <div class="card mb-4">
            <div class="card-body">
                <h4 class="card-title">Winnie</h4>
                <h6 class="card-subtitle mb-2 text-muted">Dog number 1</h6>
                <p class="card-text">Winnie is a wonderful dog!</p>
                <a href="/dogs/1/" class="btn btn-primary">Learn more about Winnie</a>
            </div>
        </div>
    
        <div class="card mb-4">
            <div class="card-body">
                <h4 class="card-title">Mosely</h4>
                <h6 class="card-subtitle mb-2 text-muted">Dog number 2</h6>
                <p class="card-text">Mosely is a wonderful dog!</p>
                <a href="/dogs/2/" class="btn btn-primary">Learn more about Mosely</a>
            </div>
        </div>
    
        <div class="card mb-4">
            <div class="card-body">
                <h4 class="card-title">Bella</h4>
                <h6 class="card-subtitle mb-2 text-muted">Dog number 3</h6>
                <p class="card-text">Bella is a wonderful dog!</p>
                <a href="/dogs/3/" class="btn btn-primary">Learn more about Bella</a>
            </div>
        </div>

</body>
</html>

django static file results

Django模板总结

在本教程中,我们看了一下Django模板语言,这是Django自己的模板系统。还有其他第三方模板系统可用于Django工作,但最好还是使用DTL,因为它是Django的标准。使用DTL将使你更容易创建可插入其他项目的应用程序,并帮助你创建更多可重复使用的模板。