Django - 模板

  • 作为一个网络框架,Django 需要一种方便的方式来动态生成 HTML。最常见的方法是依靠模板。一个模板包含了所需 HTML 输出的静态部分,以及一些特殊的语法,描述了如何插入动态内容。关于使用模板创建 HTML 页面的实战例子,请看 教程 3
  • 一个 Django 项目可以配置一个或多个模板引擎(如果你不使用模板,甚至可以不配置模板)。Django 内置了自己的模板系统后端,创造性地称为 Django 模板语言(DTL),以及流行的替代版本 Jinja2。其他模板语言的后端可以从第三方获得。你也可以编写自己的自定义后端,参见:自定义模板后端
  • Django 定义了一个标准的 API,用于加载和渲染模板,而不考虑后端。加载包括为给定的标识符找到模板并对其进行预处理,通常是将其编译成内存中的表示形式。渲染是指将上下文数据插入模板,并返回结果字符串。
  • Django 模板语言 是 Django 自己的模板系统。在 Django 1.8 之前,它是唯一的内置选项。它是一个很好的模板库,尽管它是相当有主见的,并且有一些特殊的地方。如果你没有迫切的理由选择另一个后端,你应该使用 DTL,特别是当你正在编写一个可插拔的应用程序,并且你打算发布模板时。Django 的 contrib 应用如果包含模板,比如 django.contrib.admin,就使用 DTL。
  • 由于历史原因,模板引擎的通用支持和 Django 模板语言的实现都在 django.template 的命名空间中。
  • 模板引擎是通过 TEMPLATES 进行配置。这是一个配置列表,每个引擎都有一个。默认值为空。startproject 命令生成的 settings.py 定义了一个更有用的值:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    TEMPLATES = [
    {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],
    'APP_DIRS': True,
    'OPTIONS': {
    # ... some options here ...
    },
    },
    ]
  • BACKEND 是实现 Django 模板后端 API 的模板引擎类的点分隔 Python 路径。内置的后端有 django.template.backends.django.DjangoTemplatesdjango.template.backends.jinja2.Jinja2
  • 由于大多数引擎都是从文件中加载模板,因此每个引擎的顶层配置都包含两个常见的配置:
    • DIRS 定义了目录列表,引擎应在其中按搜索顺序查找模板源文件。
    • APP_DIRS 告诉引擎是否应该在已安装的应用程序中寻找模板。每个后端都为应用程序中存储模板的子目录定义了一个惯用名称。
  • 虽然不常见,但可以使用不同的选项配置同一后端的多个实例。 在这种情况下,你应该为每个引擎定义一个唯一的 NAME
  • OPTIONS 包含特定于后端的配置。
  • 模板

    • 概述

      • 模板是一个文本文件。它可以生成任何基于文本的格式(HTML、XML、CSV 等)。
      • 一个模板包含 变量标签,前者在模板被执行时被替换为值,后者控制模板的逻辑。
    • 示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      {% extends "base_generic.html" %}

      {% block title %}{{ section.title }}{% endblock %}

      {% block content %}
      <h1>{{ section.title }}</h1>

      {% for story in story_list %}
      <h2>
      <a href="{{ story.get_absolute_url }}">
      {{ story.headline|upper }}
      </a>
      </h2>
      <p>{{ story.tease|truncatewords:"100" }}</p>
      {% endfor %}
      {% endblock %}
  • 变量

    • 概述

      • 变量看起来像这样:{{ variable }}。 当模板引擎遇到一个变量时,它会计算该变量并将其替换为结果。 变量名称由字母数字字符和下划线(_)的任意组合组成,但不能以下划线开头,也不能是数字。 点 (.) 也出现在可变部分中,尽管它具有特殊含义,如下所示。 重要的是,变量名中不能有空格或标点符号。
      • 使用点号(.)来访问一个变量的属性。
    • 示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      from datetime import datetime

      from django.shortcuts import render


      class Movie:
      def __init__(self, name, director):
      self.name = name
      self.director = director

      def __str__(self):
      return self.name


      # Create your views here.
      def index(request):
      name = 'admin'
      dtime = datetime.now()
      movies = ['僵尸先生', '速度与激情8', '中国机长', '我和我的祖国', '复联4']
      stars = {'boy': '蔡徐坤', 'girl': '乔碧萝'}
      movie_obj = Movie('攀登者', '李仁港')
      return render(request, 'index.html', context={
      'name': name, 'dtime': dtime, 'num': 100, 'score': 7.8, 'movies': movies, 'stars': stars, 'movie_obj': movie_obj
      })

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>首页</title>
      </head>
      <body>
      欢迎:{{ name }},当前时间是:{{ dtime }}
      <br>

      访问量:{{ num }},评分:{{ score }}
      <hr>

      喜欢的电影:
      {#{{ movies.3 }}#}
      <ul>
      {% for movie in movies %}
      <li>{{ movie }}</li>
      {% endfor %}
      </ul>
      <hr>

      喜欢的网红:
      男:{{ stars.boy }}
      女:{{ stars.girl }}
      <hr>

      电影名称:{{ movie_obj.name }}
      导演:{{ movie_obj.director }}

      </body>
      </html>
  • 过滤器

    • 概述

      • 你可以通过使用 过滤器 修改显示的变量。
      • 过滤器是这样的: {{ name|lower }}。这将显示 lower 过滤器过滤后的 {{ name }} 变量的值,该过滤器将文本转换为小写。使用管道(|)来应用过滤器。
      • 过滤器可以 “链式的”。一个过滤器的输出被应用到下一个过滤器。{{ text|escape|linebreaks }} 是一个常用的成语,用于转义文本内容,然后将换行符转换为 <p> 标签。
      • 有些过滤器需要参数。一个过滤器的参数是这样的: {{ bio|truncatewords:30 }}。这将显示 bio 变量的前 30 个字。
      • 包含空格的过滤器参数必须加引号;例如,要连接一个包含逗号和空格的列表,你可以使用 {{ list|join:", " }}
      • Django 提供了大约 60 个内置的模板过滤器。你可以在 内置过滤器参考 中阅读它们。
    • default

      如果一个变量为 false 或空,使用给定的默认值。否则,使用变量的值。例如:

      1
      {{ value|default:"nothing" }}

      如果 value 没有提供或者为空,那么他将显示为 “nothing”。

    • length

      返回值的长度。这对字符串和列表都有效。例如:

      1
      {{ value|length }}

      如果 value['a', 'b', 'c', 'd'], 那么他将被显示为 4

    • filesizeformat

      以 “人类可读” 的文件大小格式化该值(即 '13 KB''4.1 MB''102 bytes' 等)。例如:

      1
      {{ value|filesizeformat }}

      如果 value 是 123456789,则输出为 117.7 MB

    • lower

      将一个字符串转换为全小写。

      1
      {{ value|lower }}

      如果 valueTotally LOVING this Album!,则输出为 totally loving this album!

    • upper

      将一个字符串转换为全大写。

      1
      {{ value|upper }}

      如果 value"Joel is a slug",则输出将是 "JOEL IS A SLUG"

    • title

      通过使单词以大写字母开头,其余字符以小写字母开头,将字符串转换为大写字母。这个标签不会努力让 “琐碎的单词” 保持小写。

      1
      {{ value|title }}

      如果 value"my FIRST post",则输出将是 "My First Post"

    • capfirst

      将值的第一个字符大写。如果第一个字符不是字母,这个过滤器就没有效果。

      1
      {{ value|capfirst }}

      如果 value"django",则输出为 "Django"

    • floatformat

      当不使用参数时,将浮点数四舍五入到小数点后一位 —— 但只在有小数部分要显示的情况下。

      value 模板 输出
      34.23234 {{ value|floatformat }} 34.2
      34.00000 {{ value|floatformat }} 34
      34.26000 {{ value|floatformat }} 34.3

      如果与数字整数参数一起使用,floatform 将一个数字四舍五入到小数点后几位。

      value 模板 输出
      34.23234 {{ value|floatformat:3 }} 34.232
      34.00000 {{ value|floatformat:3 }} 34.000
      34.26000 {{ value|floatformat:3 }} 34.260

      特别有用的是传递 0(零)作为参数,它将把浮点数舍入到最接近的整数。

      value 模板 输出
      34.23234 {{ value|floatformat:"0" }} 34
      34.00000 {{ value|floatformat:"0" }} 34
      39.56000 {{ value|floatformat:"0" }} 40
    • random

      从给定列表中随机返回一个项目。

      1
      {{ value|random }}

      如果 value 是列表 ['a', 'b', 'c', 'd'],输出可能是 "b"

    • join

      用字符串连接一个列表,就像 Python 的 str.join(list) 一样。

      1
      {{ value|join:" // " }}

      如果 value 是列表 ['a', 'b', 'c'],输出将是字符串 "a // b // c"

    • slice

      返回列表的一个片段。

      1
      {{ some_list|slice:":2" }}

      如果 some_list['a', 'b', 'c'],输出将是 ['a', 'b']

    • first

      返回列表中的第一个项目。

      1
      {{ value|first }}

      如果 value 是列表 ['a', 'b', 'c'],则输出为 'a'

    • last

      返回列表中的最后一项。

      1
      {{ value|last }}

      如果 value 是列表 ['a', 'b', 'c', 'd'],输出将是字符串 "d"

    • cut

      从给定的字符串中删除参数的所有值。

      1
      {{ value|cut:" " }}

      如果 value"String with spaces",输出将是 "Stringwithspaces"

    • date

      根据给定的格式设置日期。

      1
      {{ value|date:"Y/m/d" }}

      如果 value 是一个 datetime 对象(例如,datetime.datetime.datetime.now() 的结果),输出将是字符串 '2022/12/13'

    • time

      根据给定的格式对时间进行格式化。

      1
      {{ value|time:"H:i:s" }}

      如果 value 相当于 datetime.datetime.now(),输出将是字符串 "16:02:49"

    • add

      将参数添加到值中。

      1
      {{ value|add:"2" }}

      如果 value4,那么输出将是 6

      1
      {{ first|add:second }}

      如果 first[1, 2, 3] 并且 second[4, 5, 6],则输出为 [1, 2, 3, 4, 5, 6]

    • safe

      标记一个字符串在输出前不需要进一步的 HTML 转义。当自动转义关闭时,该过滤器没有效果。

    • escape

      转义字符串的 HTML。具体来说,它可以进行这些替换。

  • 标签

    • 概述

      • 标签是这样的: {% tag %}。标签比变量更复杂。有的在输出中创建文本,有的通过执行循环或逻辑来控制流程,有的将外部信息加载到模板中,供以后的变量使用。
      • 有些标签要求有开始和结束标签。
      • Django 有二十多个内置的模板标签。你可以在 内置标签参考 中阅读所有关于它们的信息。
    • autoescape

      • 控制当前的自动转义行为。该标签以 onoff 作为参数,决定块内是否有自动转义行为。此区块以 endautoescape 结束标签关闭。
      • 当自动转义生效时,所有的变量内容在将结果放入输出之前(但在任何过滤器被应用之后)都会被应用 HTML 转义。这相当于对每个变量手动应用 escape 过滤器。
      • 唯一的例外是那些已经被标记为 “安全” 的变量,这些变量可以是被填充变量的代码标记的,也可以是被应用了 safeescape 过滤器的。
      • 示例
        1
        2
        3
        {% autoescape on %}
        {{ body }}
        {% endautoescape %}
    • for

      • 循环浏览数组中的每个项目,使该项目在上下文变量中可用。

        1
        2
        3
        4
        5
        <ul>
        {% for athlete in athlete_list %}
        <li>{{ athlete.name }}</li>
        {% endfor %}
        </ul>

        你可以通过使用 {% for obj in list reversed %} 来反向循环一个列表。

      • 如果你需要访问字典中的项目,这也很有用。

        1
        2
        3
        {% for key, value in data.items %}
        {{ key }}: {{ value }}
        {% endfor %}

        请记住,对于点运算符来说,字典键查询优先于方法查询。因此,如果 data 字典中包含一个名为 'items' 的键,data.items 将返回 data['items'] 而不是 data.items()。如果你想在模板中使用这些方法,避免添加像字典方法一样命名的键(itemsvalueskeys 等)。在 模板变量的文档 中阅读更多关于点运算符的查找顺序。

      • for 循环设置了一组可以在循环体内直接使用的变量:

        变量名 描述
        forloop.counter 循环计数器,表示当前循环的索引(从 1 开始)。
        forloop.counter0 循环计数器,表示当前循环的索引(从 0 开始)。
        forloop.revcounter 反向循环计数器(以最后一次循环为 1,反向计数)。
        forloop.revcounter0 反向循环计数器(以最后一次循环为 0,反向计数)。
        forloop.first 当前循环为首个循环时,该变量为 True
        forloop.last 当前循环为最后一个循环时,该变量为 True
        forloop.parentloop 在嵌套循环中,指向当前循环的上级循环
    • for ... empty

      当传递到 for 标签中的数组不存在或为空时,可以使用 {% empty %} 标签来指定输出的内容

      1
      2
      3
      4
      5
      6
      7
      <ul>
      {% for athlete in athlete_list %}
      <li>{{ athlete.name }}</li>
      {% empty %}
      <li>Sorry, no athletes in this list.</li>
      {% endfor %}
      </ul>
    • if

      {% if %} 标签会判断给定的变量,当变量为 True 时(比如存在、非空、非布尔值 False),就会输出块内的内容

      1
      2
      3
      4
      5
      6
      7
      {% if athlete_list %}
      Number of athletes: {{ athlete_list|length }}
      {% elif athlete_in_locker_room_list %}
      Athletes should be out of the locker room soon!
      {% else %}
      No athletes.
      {% endif %}

      正如你所看到的,if 标签可能带有一个或多个 {% elif %} 分支,以及一个 {% else %} 分支。当 {% else %} 之前的所有分支条件都不满足时,{% else %} 分支的内容会被显示出来。所有的分支都是可选的。

    • ifequalifnotequal

      3.1 版后已移除
      1
      2
      3
      4
      {% ifequal a b %} ... {% endifequal %}
      是写
      {% if a == b %} ... {% endif %}
      的过时方式
      1
      2
      3
      4
      {% ifnotequal a b %} ... {% endifnotequal %}

      {% if a != b %} ... {% endif %}
      取代
    • ifchanged

      检查一个值是否在循环的最后一次迭代中发生了变化。

    • block

      定义一个可以被子模板覆盖的块。更多信息请参见 模板继承

    • extends

      表示该模板扩展了一个父模板。
      这个标签有两种使用方式:

      1
      2
      3
      4
      5
      # (带引号)使用字面值 "base.html" 作为要扩展的父模板的名称。
      {% extends "base.html" %}

      # 使用 variable 的值。如果变量的值是一个字符串,Django 将使用这个字符串作为父模板的名称。如果变量的值是一个 Template 对象,Django 将使用该对象作为父模板。
      {% extends variable %}
    • url

      返回与给定视图和可选参数相匹配的绝对路径引用(不含域名的 URL)。路径中的任何特殊字符将使用 iri_to_uri() 进行编码。

      1
      {% url "namespace:name" 参数1 …… %}
    • csrf_token

      这个标签用于 CSRF 保护,如 跨站点请求伪造 的文档中所述。

      1
      {% csrf_token %}
    • load

      加载一个自定义模板标签集。

      1
      {% load static %}
  1. 创建 static 目录

    在工程目录下创建名为 static 的目录作为静态文件的存放位置

  2. 配置 settings.py 文件

    1
    2
    STATIC_URL = "static/"
    STATICFILES_DIRS = [BASE_DIR / "static"]
  3. 保存静态文件

    将你的静态文件保存至项目中名为 static 的目录中。如:static/images/123.gif

  4. 加载静态文件

    1
    2
    3
    {% load static %}

    <img src="{% static 'images/123.gif' %}" alt="">
  5. 部署

    • 概述
      django.contrib.staticfiles 提供了一个便利的管理命令,用于将静态文件收集至独立目录,方便你为它们提供服务。
    1. STATIC_ROOT 配置成你喜欢的目录,在这个目录提供服务,例如:
      1
      STATIC_ROOT = "/www/wwwroot/example.com/static/"

      settings.py 文件中添加以上内容

    2. 运行 collectstatic 管理命令
      1
      python manage.py collectstatic

      这将会把静态目录下的所有文件拷贝至 STATIC_ROOT 目录。

    3. 选一个 Web 服务器为这些文件提供服务。 文档 如何部署静态文件 介绍了静态文件的常见部署策略。