0x30 -- Django模版层 -- 模版基础


模板官网链接

0x00 – 模版基础

  • 一、作为一个网络框架,Django 需要一种方便的方式来动态生成 HTML。

    1. 最常见的方法是依靠模板。
    2. 一个模板包含了所需 HTML 输出的静态部分,以及一些特殊的语法,描述了如何插入动态内容。
  • 二、 一个 Django 项目可以配置一个或多个模板引擎(如果你不使用模板,甚至可以不配置模板)。

    1. Django 内置了自己的模板系统后端,创造性地称为 Django 模板语言(DTL),以及流行的替代版本 Jinja2
    2. 其他模板语言的后端可以从第三方获得。
    3. 你也可以编写自己的自定义后端,参见:自定义模板后端
  • 三、Django 定义了一个标准的API,用于加载和渲染模板,而不考虑后端。

    1. 加载包括为给定的标识符找到模板并对其进行预处理,通常是将其编译成内存中的表示形式。
    2. 渲染是指将上下文数据插入模板,并返回结果字符串。
  • 四、Django 模板语言是 Django 自己的模板系统。

    1. 在 Django 1.8 之前,它是唯一的内置选项。
    2. 它是一个很好的模板库,尽管它是相当有主见的,并且有一些特殊的地方。
    3. 如果你没有迫切的理由选择另一个后端,你应该使用 DTL,特别是当你正在编写一个可插拔的应用程序,并且你打算发布模板时。Django 的 contrib 应用如果包含模板,比如 django.contrib.admin,就使用 DTL。

由于历史原因,模板引擎的通用支持和 Django 模板语言的实现都在 django.template 的命名空间中。

警告
模板系统对于不受信任的模板作者并不安全。例如,一个网站不应该允许其用户提供自己的模板,因为模板作者可以做一些事情,如执行 XSS 攻击和访问可能包含敏感信息的模板变量的属性。


0x01 – 语法

这是对 Django 模板语言语法的概述。详细信息请参见 语言语法参考

  1. Django 模板是使用 Django 模板语言标记的一个文本文档或Python字符串。
  2. 模板引擎可以识别和解释一些构造。主要是变量和标签。
  3. 模板是通过上下文来渲染的。渲染用变量的值替换变量,变量的值在上下文中查找,并执行标签。其他的一切都按原样输出。

小总结开始

Django 模板语言的语法涉及四个构造。

  1. 变量:放在 {{}} 之间;
  2. 标签:放在 {%%} 之间;
  3. 过滤器:用 |隔开的,例如:{{ value|add:“2” }},意思是将参数添加到值中,过滤取很多,自取
  4. 注释:
    1. 单行:{# 这里是单行注释 #}
    2. 多行:{% comment %} <p>这里是被注释的多行p标签</p> {% endcomment %}

小总结结束


0x01 – 1 – 变量

变量从上下文中输出一个值,上下文是一个类似于字典的对象,将键映射到值。

变量被 {{}} 包围,如下所示:

My first name is {{ first_name }}. My last name is {{ last_name }}.

在上下文为 {'first_name': 'John', 'last_name': 'Doe'} 的情况下,该模板渲染为:

My first name is John. My last name is Doe.

字典查找,属性查找和列表索引查找均以点符号实现:

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

如果变量解析为可调用对象,则模板系统将不带任何参数的情况下调用它,并使用其结果代替可调用对象。


0x01 – 2 – 标签

标签在渲染过程中提供了任意逻辑。

这个定义是故意含糊的。例如,标签可以输出内容,或用作控制结构如 “if” 语句和 “for” 循环,或从数据库中抓取内容,甚至可以访问其他模板标签。

标签被 {%%} 包围,如下所示:

{% csrf_token %}

大多数标签都接受参数:

{% cycle 'odd' 'even' %}

一些标签需要开始和结束标签:

{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}

提供 内置标签参考 以及 自定义标签编写指南


0x01 – 3 – 过滤器

过滤器转换变量和标签参数的值。

它们看起来像这样:

{{ django|title }}

{'django': 'the web framework for perfectionists with deadlines'} 的上下文中,这个模板渲染为:

The Web Framework For Perfectionists With Deadlines

有些过滤器需要一个参数:

{{ my_date|date:"Y-m-d" }}

提供 内建过滤器参考 以及 自定义过滤器编写指南.


0x01 – 4 – Comments

注释看起来像这样:

{# this won't be rendered #}

{% comment %} 标签提供多行注释。


0x02 – 组件

关于本节

这是对 Django 模板语言 API 的概述。详细信息请参见 API 参考


0x02 – 1 – 引擎

django.template.Engine 封装了 Django 模板系统的实例。
直接实例化 Engine 的主要原因是为了在 Django 项目之外使用 Django 模板语言。
django.template.backends.django.DjangoTemplates 是一个简单封装,使 django.template.Engine 适应 Django 的模板后端API。


0x02 – 2 – 模板

django.template.Template 代表已编译的模板。

模板可以通过 Engine.get_template()Engine.from_string() 获得。

同样 django.template.backends.django.Template 是一个简单封装,使 django.template.Template 适应通用模板 API。


0x02 – 3 – 上下文

django.template.Context 除了上下文数据外,还保存了一些元数据。它被传递给 Template.render() 来渲染模板。

django.template.RequestContextContext 的子类,它储存当前的 HttpRequest 并运行模板上下文处理器。

通用 API 没有对应的概念。上下文数据以普通的 dict 传递,而当前的 HttpRequest 则根据需要单独传递。


0x02 – 4 – 加载器

模板加载器负责定位模板,加载模板,并返回 Template 对象。

Django 提供了几个 内建模板加载器 并且支持 自定义模板加载器


0x02 – 5 – 上下文处理器

上下文处理器是接收当前的 HttpRequest 作为参数,并返回一个 dict 的数据添加到渲染上下文的函数。

它们的主要用途是将所有模板共享的通用数据添加到上下文中,而无需在每个视图中重复代码。

Django 提供了许多 内置上下文处理器,你也可以实现自己的其他上下文处理器。



0x03 – 模板引擎的支持

0x03 – 1 – 配置

模板引擎是通过 TEMPLATES 进行配置。这是一个配置列表,每个引擎都有一个。默认值为空。startproject 命令生成的 settings.py 定义了一个更有用的值:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

由于大多数引擎都是从文件中加载模板,因此每个引擎的顶层配置都包含两个常见的配置:

  • DIRS 定义了目录列表,引擎应在其中按搜索顺序查找模板源文件。
  • APP_DIRS 告诉引擎是否应该在已安装的应用程序中寻找模板。每个后端都为应用程序中存储模板的子目录定义了一个惯用名称。

虽然不常见,但可以使用不同的选项配置同一后端的多个实例。 在这种情况下,你应该为每个引擎定义一个唯一的 NAME

  • OPTIONS 包含特定于后端的配置。

0x03 – 2 – 用法

django.template.loader 模块定义了两个加载模板的函数。

  1. get_template(template_name, using=None)

    • 此函数使用给定名称加载模板并返回 Template 对象。
    • 返回值的确切类型取决于加载模板的后端。
    • 每个后端都有自己的 Template 类。
    • get_template() 依次尝试每个模板引擎,直到成功为止。
    • 如果找不到模板,则会引发 TemplateDoesNotExist 错误。
    • 如果找到模板但包含无效语法,则会引发 TemplateSyntaxError 错误。
    • 搜索和加载模板的方式取决于每个引擎的后端和配置。
    • 如果你想把搜索限制在一个特定的模板引擎上,在 using 参数中传递该引擎的 NAME
  2. select_template(template_name_list, using=None)

    • select_template() 就像 get_template(),不同的是,它接受一个模板名称的列表。

    • 它按顺序尝试每个名字,并返回第一个存在的模板。

如果加载模板失败,则可能会引发在 django.template 中定义的以下两个异常:

  1. exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)

    • 当找不到模板时引发此异常。 它接受以下可选参数在调试页面上填充 模板事后检验
      • backend产生异常的模板后端实例。
      • tried查找模板时尝试过的来源列表。它的格式为包含 (origin, status) 的元组列表,其中 origin 是一个 类 origin 对象而 status 是一个说明找不到模板原因的字符串。
      • chain尝试加载模板时引发的一系列中间 TemplateDoesNotExist 异常列表。这由函数使用,例如:get_template(),这些函数尝试从多个引擎加载给定的模板。
  2. exception TemplateSyntaxError(msg)

    • 当找到模板但包含错误时,将引发此异常。

get_template()select_template() 返回的 Template 对象必须提供具有以下签名的 render()` 方法:

  • Template.``render(context=None, request=None)

    使用给定的上下文渲染此模板。

    如果提供了 context ,则必须是 dict。如果未提供,则引擎将使用空上下文渲染模板。

    如果提供了 request,它必须是 HttpRequest。同时引擎必须使它和 CSRF 令牌在模板中可用。如何实现这一点由每个后端决定。

下面是一个搜索算法的例子。在这个例子中 TEMPLATES 设置为:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

如果你调用 get_template('story_detail.html'),以下是 Django 将按顺序查找的文件:

  • /home/html/example.com/story_detail.html'django' 引擎)
  • /home/html/default/story_detail.html'django' 引擎)
  • /home/html/jinja2/story_detail.html'jinja2' 引擎)

如果你调用 select_template(['story_253_detail.html', 'story_detail.html']),Django 将寻找以下内容:

  • /home/html/example.com/story_253_detail.html'django' 引擎)
  • /home/html/default/story_253_detail.html'django' 引擎)
  • /home/html/jinja2/story_253_detail.html'jinja2' 引擎)
  • /home/html/example.com/story_detail.html'django' 引擎)
  • /home/html/default/story_detail.html'django' 引擎)
  • /home/html/jinja2/story_detail.html'jinja2' 引擎)

当 Django 发现一个模板存在时,它就会停止寻找。

提示
你可以使用 select_template() 灵活的加载模板。例如,如果你写了一个新闻故事,并希望一些故事有自定义模板,使用类似 select_template(['story_%s_detail.html' % story.id, 'story_detail.html']) 。这将允许你为单个故事使用自定义模板,为没有自定义模板的故事使用备用模板。

可以——而且最好是——在每个包含模板的目录内的子目录中组织模板。惯例是为每个 Django 应用程序创建一个子目录,根据需要在这些子目录中包含子目录。

这样做是为了你自己的理智。将所有模板存储在一个目录的根级别会很麻烦。

要加载子目录中的模板,请使用斜杠,如下所示:

get_template('news/story_detail.html')

使用与上述相同的 TEMPLATES 选项,这将尝试加载以下模板:

  • /home/html/example.com/news/story_detail.html'django' 引擎)
  • /home/html/default/news/story_detail.html'django' 引擎)
  • /home/html/jinja2/news/story_detail.html'jinja2' 引擎)

此外,为了减少加载和渲染模板的重复性,Django 提供了一个自动处理的快捷函数。

  • render_to_string(template_name, context=None, request=None, using=None)

    render_to_string() 加载一个模板 get_template() ,并立即调用它的 render() 方法。它需要下面的参数。

    template_name加载和呈现模板的名称。如果是模板名称列表,Django 使用 select_template() ,而不是 get_template() 找到模板。

    context dict 用作模板的渲染上下文。

    request 可选项 HttpRequest 在模板的渲染过程中可用。

    using可选的模板引擎 NAME。对模板的搜索将限于该引擎。使用实例:from django.template.loader import render_to_string rendered = render_to_string('my_template.html', {'foo': 'bar'})

还可以参看 render() 快捷函数,它调用 render_to_string() ,并将结果提供给 HttpResponse ,适合从视图返回。

最后,您可以直接使用配置好的引擎:

  • engines

    模板引擎可在 django.template.engines 中使用:from django.template import engines django_engine = engines['django'] template = django_engine.from_string("Hello {{ name }}!")在这个例子中,查找关键字“django”是引擎的 NAME


0x03 – 3 – 内置后端

  • class DjangoTemplates

设置 BACKEND'django.template.backends.django.DjangoTemplates',以配置 Django 模板引擎。

APP_DIRSTrue 时,DjangoTemplates 引擎会在已安装应用程序的 templates 子目录中寻找模板。这个通用名称是为了向后兼容而保留的。

DjangoTemplates 引擎接受以下 OPTIONS:

  • 'autoescape':一个布尔值,控制是否启用 HTML 自动转义。

    默认为 True

    警告

    只有当你渲染非 HTML 模板时,才将其设置为 False

  • 'context_processors':当模板被请求渲染时,用于填充上下文的可调用项的点分隔 Python 路径列表。这些可调用的对象以一个请求对象作为参数,并返回一个 dict 的项目,这些项目将被合并到上下文中。

    默认为空列表。

    查看 RequestContext 获取更多信息。

  • 'debug':开启/关闭模板调试模式的布尔值。如果是 True,错误页面将显示模板渲染过程中出现的任何异常的详细报告。该报告包含模板的相关片段,并突出显示相应的行。

    默认为 DEBUG 配置的值。

  • 'loaders':模板加载器类的点分隔 Python 路径列表。每个 Loader 类都知道如何从特定源导入模板。可以选择使用元组来代替字符串。元组中的第一项应该是 Loader 类名,随后的项在初始化期间传递给 Loader

    默认值取决于 DIRSAPP_DIRS 的值。

    查看 加载器类型 获取详细信息。

  • 'string_if_invalid':模板系统对无效变量(如拼写错误)应将此字符串输出。

    默认为空字符串。

    查看 如何处理无效变量 获取更多信息。

  • 'file_charset':用于读取磁盘上模板文件的字符集。

    默认为 'utf-8'

  • 'libraries':模板标签模块的标签字典和点分隔 Python 路径,用于向模板引擎注册。 这可用于添加新库或为现有库提供替代标签。例如:

    OPTIONS={
        'libraries': {
            'myapp_tags': 'path.to.myapp.tags',
            'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
        },
    }
    

    可以通过将相应的字典键传递到 {% load %} 标签来加载库。

  • 'builtins':要添加的 内置模板标签和过滤器 的点分隔 Python 路径列表。例如:

    OPTIONS={
        'builtins': ['myapp.builtins'],
    }
    

    可以使用内置库中的标签和过滤器,而不需要先调用 {% load %} 标签。

  • class Jinja2

需要安装 Jinja2

$ python -m pip install Jinja2

设置 BACKEND'django.template.backends.jinja2.Jinja2' 以配置一个 Jinja2 引擎。

APP_DIRSTrue 时,Jinja2 引擎会在安装的应用程序的 jinja2 子目录中查找模板。

OPTIONS 中最重要的条目是 'environment'。它是一个点分隔 Python 路径,指向一个返回 Jinja2 环境的可调用对象。默认为 'jinja2.Environment'。Django 调用该可调用对象并传递其他选项作为关键字参数。此外,Django 为一些选项添加了不同于 Jinja2 的默认值。

  • 'autoescape'True
  • 'loader':为 DIRSAPP_DIRS 配置的加载器
  • 'auto_reload'settings.DEBUG
  • 'undefined'DebugUndefined if settings.DEBUG else Undefined

Jinja2 引擎也接受以下 OPTIONS

  • 'context_processors':当模板被请求渲染时,用于填充上下文的可调用项的点分隔 Python 路径列表。这些可调用的对象以一个请求对象作为参数,并返回一个 dict 的项目,这些项目将被合并到上下文中。

    默认为空列表。

    不建议将上下文处理器与 Jinja2 模板一起使用。

    上下文处理器在 Django 模板中很有用,因为 Django 模板不支持调用带参数的函数。由于 Jinja2 没有此限制,因此建议将你要用作上下文处理器的函数放在模板的全局变量 jinja2.Environment 中使用,如下所述。然后你可以在模板中调用该函数。

    {{ function(request) }}
    

    有些 Django 模板的上下文处理器会返回一个固定的值。对于 Jinja2 模板,不需要这一层间接操作,因为您可以直接在 jinja2.Environment 中添加常量。

    最初为 Jinja2 增加上下文处理器的用例涉及:

    • 根据请求进行昂贵的计算。
    • 在每个模板中都需要结果。
    • 在每个模板中多次使用结果。

    除非满足所有这些条件,否则将函数传递给模板更符合 Jinja2 的设计。

默认配置被有意地保持为最低。如果一个模板被请求渲染(例如,当使用 render()),Jinja2 后端会在上下文中添加 requestcsrf_inputcsrf_token。除此之外,此后端不会创建 Django 风格的环境。 它不知道 Django 过滤器和标签。 为了使用 Django 特有的 API,你必须将它们配置到环境中。

例如,您可以使用以下内容创建 myproject/jinja2.py

from django.templatetags.static import static
from django.urls import reverse

from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': static,
        'url': reverse,
    })
    return env

并将 'environment' 选项设置为 'myproject.jinja2.environment'

这样你就可以在 Jinja2 模板中使用以下构造:

<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">

<a href="{{ url('admin:index') }}">Administration</a>

Django 模板语言和 Jinja2 中都存在标签和过滤器的概念,但使用方式不同。由于 Jinja2 支持在模板中向可调用对象传递参数,所以很多在 Django 模板中需要模板标签或过滤器的功能都可以通过在 Jinja2 模板中调用函数来实现,如上例所示。Jinja2 的全局命名空间消除了对模板上下文处理器的需求。Django 模板语言并没有与 Jinja2 测试相对应的功能。


2021年10月26日

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值