{% raw %}

Django 模板语言:面向Python程序员

本文从技术的角度解释Django 模板系统 —— 它如何工作以及如何继承它。如果你正在查找语言语法方面的参考,参见Django 模板语言

假设你已经理解了模板、上下文、变量、标签和渲染。如果你不熟悉这些概念,从阅读 Django 模板语言起步吧。

概述

在Python中使用模板系统有三个步骤:

  1. 配置引擎
  2. 将模板代码编译成模板
  3. 根据上下文渲染模板。

对于这些步骤,Django 的项目一般使用高级的、与后端无关的API ,而不用模板系统的底层API:

  1. Django?对TEMPLATES ?设置中的每个DjangoTemplates 后端实例化一个引擎DjangoTemplates 封装引擎并将它适配成通用的模板后端API。
  2. django.template.loader 模块提供一些函数例如get_template() 来加载模板。它们返回一个django.template.backends.django.Template,这个返回值封装了真正的django.template.Template
  3. 在上一步中获得的模板 有一个render() 方法, 该方法将上下文和HTTP 请求添加到Context中并将渲染委托给底层的模板

配置引擎

class Engine([dirs][, app_dirs][, allowed_include_roots][, context_processors][, debug][, loaders][, string_if_invalid][, file_charset])

New in Django 1.8.

实例化Engine 时,所有的参数必须通过关键字参数传递:

  • dirs 是一个列表,包含引擎查找模板源文件的目录。它用于配置filesystem.Loader。
    默认为一个空的列表。

  • app_dirs 只影响loaders 的默认值。参见下文。
    默认为False

  • allowed_include_roots 是一个字符串列表,它们表示{% ssi %} 模板标签的前缀。这是一个安全措施,让模板的作者不能访问他们不应该访问的文件。
    例如,如果'allowed_include_roots'['/home/html', '/var/www'],那么{% ssi /home/html/foo.txt %} 可以工作,而{% ssi /etc/passwd %} 不能工作。
    默认为一个空的列表。
    Deprecated since version 1.8: 废弃allowed_include_roots

  • context_processors 是一个Python 可调用对象的路径列表,它们用于模板渲染时填充其上下文。这些可调用对象接收一个HTTP 请求对象作为它们的参数,并返回一个 字典 用于合并到上下文中。
    它默认为一个空的列表。
    更多信息,参见RequestContext

  • debug 是一个布尔值,表示打开/关闭模板的调试模式。如果为True,模板引擎将保存额外的调试信息,这些信息可以用来显示模板渲染过程中引发的异常的详细报告。
    它默认为False

  • loaders 是模板加载类的一个列表,这些类由字符串表示。每个Loader 类知道如何从一个特定的源导入模板。还可以使用元组代替字符串表示这些类。元组的第一个元素应该是Loader 类的名称,接下来的元素将在Loader 初始化时用于初始化。
    它默认为包含下面内容的列表:

    • 'django.template.loaders.filesystem.Loader'
    • 'django.template.loaders.app_directories.Loader’ 当前仅当app_dirsTrue

Loader types

  • string_if_invalid 表示模板系统遇到不合法(例如,拼写错误)的变量时应该使用的字符串。
    默认为空字符串。
    细节参见如何处理不合法的变量

  • file_charset 是读取磁盘上的模板文件使用的字符集。
    默认为'utf-8'

static Engine.get_default()

当Django 项目配置仅配置一个DjangoTemplates 引擎时,这个方法返回底层的引擎。在其它情况下,它将引发ImproperlyConfigured

It’s required for preserving APIs that rely on a globally available, implicitly configured engine. Any other use is strongly discouraged.

Engine.from_string(template_code)

编译给定的template_code 并返回一个Template 对象。

Engine.get_template(template_name)

根据给定的名称,编译并返回一个Template 对象。

Engine.select_template(self, template_name_list)

类似get_template(),不同的是它接收一个名称列表并返回找到的第一个模板。

加载模板

建议调用Engine 的工厂方法创建Templateget_template()select_template()from_string()

TEMPLATES?只定义一个DjangoTemplates 引擎的Django 项目中,可以直接实例化Template

class Template

这个类位于django.template.Template。其构造函数接收一个参数 —— 原始的模板代码:

  1. from django.template import Template
  2. template = Template("My name is {{ my_name }}.")

幕后

系统只会解析一次原始的模板代码 —— 当创建Template 对象的时候。在此之后,处于性能考虑,会在内部将它存储为一个树形结构。

解析器本身非常快。大部分解析的动作只需要调用一个简短的正则表达式。

渲染上下文

一旦编译好Template 对象,你就可以用上下文渲染它了。你可以使用不同的上下文多次重新渲染相同的模板。

class Context([dict][, currentapp])

这个类位于django.template.Context。其构造函数接收两个可选的参数:

  • 一个字典,映射变量名到变量的值。

  • 当前应用的名称。该应用的名称用于帮助解析带命名空间的URLs。如果没有使用带命名空间的URL,可以忽略这个参数。
    Deprecated since version 1.8: 废弃current_app 参数。如果需要它,则必须使用RequestContext 代替Context

细节参见下文的使用上下文对象

Template.render(context)

使用Context 调用Template 对象的render() 方法来“填充”模板:

  1. >>> from django.template import Context, Template
  2. >>> template = Template("My name is {{ my_name }}.")
  3. >>> context = Context({"my_name": "Adrian"})
  4. >>> template.render(context)
  5. "My name is Adrian."
  6. >>> context = Context({"my_name": "Dolores"})
  7. >>> template.render(context)
  8. "My name is Dolores."

变量及其查找

变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。

点在模板渲染时有特殊的含义。变量名中点表示查找。具体一点,当模板系统遇到变量名中的一个点时,它会按下面的顺序进行查找:

  • 字典查找。例如:foo["bar"]
  • 属性查找。例如:foo.bar
  • 列表索引查找。例如:foo[bar]

注意,像{{ foo.bar }} 这种模版表达式中的“bar”,如果在模版上下文中存在,将解释为一个字符串字面量而不是使用变量“bar”的值。

模板系统使用找到的第一个可用的类型。这是一个短路逻辑。下面是一些示例:

  1. >>> from django.template import Context, Template
  2. >>> t = Template("My name is {{ person.first_name }}.")
  3. >>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
  4. >>> t.render(Context(d))
  5. "My name is Joe."
  6. >>> class PersonClass: pass
  7. >>> p = PersonClass()
  8. >>> p.first_name = "Ron"
  9. >>> p.last_name = "Nasty"
  10. >>> t.render(Context({"person": p}))
  11. "My name is Ron."
  12. >>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
  13. >>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
  14. >>> t.render(c)
  15. "The first stooge in the list is Larry."

如果变量的任何部分是可调用的,模板系统将尝试调用它。例如:

  1. >>> class PersonClass2:
  2. ... def name(self):
  3. ... return "Samantha"
  4. >>> t = Template("My name is {{ person.name }}.")
  5. >>> t.render(Context({"person": PersonClass2}))
  6. "My name is Samantha."

可调用的变量比只需直接查找的变量稍微复杂一些。需要记住下面几点:

  • 如果变量在调用时引发一个异常,该异常将会传播,除非该异常的silent_variable_failure 属性的值为True。如果异常确实 具有一个silent_variable_failure 属性且值为True,该变量将渲染成引擎的string_if_invalid 配置的值(默认为一个空字符串)。例如:
  1. >>> t = Template("My name is {{ person.first_name }}.")
  2. >>> class PersonClass3:
  3. ... def first_name(self):
  4. ... raise AssertionError("foo")
  5. >>> p = PersonClass3()
  6. >>> t.render(Context({"person": p}))
  7. Traceback (most recent call last):
  8. ...
  9. AssertionError: foo
  10. >>> class SilentAssertionError(Exception):
  11. ... silent_variable_failure = True
  12. >>> class PersonClass4:
  13. ... def first_name(self):
  14. ... raise SilentAssertionError
  15. >>> p = PersonClass4()
  16. >>> t.render(Context({"person": p}))
  17. "My name is ."
  1. 注意,[`django.core.exceptions.ObjectDoesNotExist`](17b268cc94b52a5efb3bc8b6349653b1) `silent_variable_failure = True`,它是Django 数据库API 所有`DoesNotExist` 异常的基类。所以,如果你在Django 模板中使用Django 模型对象,任何 `DoesNotExist` 异常都将默默地失败。
  • 只有在变量不需要参数时才可调用。否则,系统将返回引擎的string_if_invalid 选项。

  • 很显然,调用某些变量会带来副作用,允许模板系统访问它们将是愚蠢的还会带来安全漏洞。
    每个Django 模型对象的delete() 方法就是一个很好的例子。模板系统不应该允许下面的行为:

  1. I will now delete this valuable data. {{ data.delete }}
  1. 设置可调用变量的`alters_data` 属性可以避免这点。如果变量设置`alters_data=True` ,模板系统将不会调用它,而会无条件使用`string_if_invalid` 替换这个变量。Django 模型对象自动生成的[`delete()`](bb4dc4f2a6e578e8219b4d6f1f2a5e82) 和[`save()`](988b555626b17a671e6c59c1a82c934f) 方法自动 设置`alters_data=True` 例如:
  1. def sensitive_function(self):
  2. self.database_record.delete()
  3. sensitive_function.alters_data = True
  • 有时候,处于某些原因你可能想关闭这个功能,并告诉模板系统无论什么情况下都不要调用变量。设置可调用对象的do_not_call_in_templates 属性的值为True 可以实现这点。模板系统的行为将类似这个变量是不可调用的(例如,你可以访问可调用对象的属性)。

如何处理不合法的变量

一般情况下,如果变量不存在,模板系统会插入引擎string_if_invalid 配置的值,其默认设置为''(空字符串)。

过滤器只有在string_if_invalid 设置为''(空字符串)时才会应用到不合法的变量上。如果string_if_invalid 设置为任何其它的值,将会忽略变量的过滤器。

这个行为对于ifforregroup 模板标签有些不同。如果这些模板便签遇到不合法的变量,会将该变量解释为None。在这些模板标签中的过滤器对不合法的变量也会始终应用。

如果string_if_invalid 包含'%s',这个格式标记将会被非法变量替换。

只用于调试目的!

虽然string_if_invalid 是一个很有用的调试工具,但是,将它作为“默认的开发工具”是个很糟糕的主意。

许多模板包括Admin 站点,在遇到不存在的变量时,依赖模板系统的沉默行为。如果你赋值非'' 的值给string_if_invalid,使用这些模板和站点可能遇到渲染上的问题。

一般情况下,只有在调试一个特定的模板问题时才启用string_if_invalid,一旦调试完成就要清除。

内置的变量

每个上下文都包含TrueFalseNone。和你期望的一样,这些变量将解析为对应的Python 对象。

字符串字面值的局限

Django 的模板语言没有办法转义它自己的语法用到的字符。例如,如果你需要输出字符序列{%%},你需要用到templatetag 标签。

如果你想在模板过滤器或标签中包含这些序列,会存在类似的问题。例如,当解析block 标签时,Django 的模板解析器查找%} 之后出现的第一个{%。这将导致不能使用"%}" 这个字符串字面量。例如,下面的表达式将引发一个TemplateSyntaxError

  1. {% include "template.html" tvar="Some string literal with %} in it." %}
  2. {% with tvar="Some string literal with %} in it." %}{% endwith %}

在过滤器参数中使用反向的序列会触发同样的问题:

  1. {{ some.variable|default:"}}" }}

如果你需要使用这些字符串序列,可以将它们保存在模板变量中,或者自定义模板标签或过滤器来绕过这个限制。

使用Context 对象

大部分时候,你将通过传递一个完全填充的字典给Context() 来实例化一个Context 对象。你也可以使用标准的字典语法在Context 对象实例化之后,向它添加和删除元素:

  1. >>> from django.template import Context
  2. >>> c = Context({"foo": "bar"})
  3. >>> c['foo']
  4. 'bar'
  5. >>> del c['foo']
  6. >>> c['foo']
  7. Traceback (most recent call last):
  8. ...
  9. KeyError: 'foo'
  10. >>> c['newvariable'] = 'hello'
  11. >>> c['newvariable']
  12. 'hello'

Context.get(key, otherwise=None)

如果key 在Context 中,则返回key 的值,否则返回otherwise

Context.pop()

Context.push()

exception ContextPopException

Context 对象是一个栈。也就是说,你可以push()pop() 它。如果你pop() 得太多,它将引发django.template.ContextPopException

  1. >>> c = Context()
  2. >>> c['foo'] = 'first level'
  3. >>> c.push()
  4. {}
  5. >>> c['foo'] = 'second level'
  6. >>> c['foo']
  7. 'second level'
  8. >>> c.pop()
  9. {'foo': 'second level'}
  10. >>> c['foo']
  11. 'first level'
  12. >>> c['foo'] = 'overwritten'
  13. >>> c['foo']
  14. 'overwritten'
  15. >>> c.pop()
  16. Traceback (most recent call last):
  17. ...
  18. ContextPopException

New in Django 1.7.

你还可以使用push() 作为上下文管理器以确保调用对应的pop()

  1. >>> c = Context()
  2. >>> c['foo'] = 'first level'
  3. >>> with c.push():
  4. ... c['foo'] = 'second level'
  5. ... c['foo']
  6. 'second level'
  7. >>> c['foo']
  8. 'first level'

所有传递给push() 的参数将传递给dict 构造函数用于构造新的上下文层级。

  1. >>> c = Context()
  2. >>> c['foo'] = 'first level'
  3. >>> with c.push(foo='second level'):
  4. ... c['foo']
  5. 'second level'
  6. >>> c['foo']
  7. 'first level'

Context.update(other_dict)

除了push()pop() 之外,Context 对象还定义一个update() 方法。它的工作方式类似push(),不同的是它接收一个字典作为参数并将该字典压入栈。

  1. >>> c = Context()
  2. >>> c['foo'] = 'first level'
  3. >>> c.update({'foo': 'updated'})
  4. {'foo': 'updated'}
  5. >>> c['foo']
  6. 'updated'
  7. >>> c.pop()
  8. {'foo': 'updated'}
  9. >>> c['foo']
  10. 'first level'

Context 用作栈在一些自定义的标签中 非常方便。

Context.flatten()

New in Django 1.7.

利用flatten() 方法,你可以获得字典形式的全部Context 栈,包括内建的变量。

  1. >>> c = Context()
  2. >>> c['foo'] = 'first level'
  3. >>> c.update({'bar': 'second level'})
  4. {'bar': 'second level'}
  5. >>> c.flatten()
  6. {'True': True, 'None': None, 'foo': 'first level', 'False': False, 'bar': 'second level'}

flatten() 方法在内部还被用来比较Context 对象。

  1. >>> c1 = Context()
  2. >>> c1['foo'] = 'first level'
  3. >>> c1['bar'] = 'second level'
  4. >>> c2 = Context()
  5. >>> c2.update({'bar': 'second level', 'foo': 'first level'})
  6. {'foo': 'first level', 'bar': 'second level'}
  7. >>> c1 == c2
  8. True

在单元测试中,flatten() 的结果可以用于比较Contextdict

  1. class ContextTest(unittest.TestCase):
  2. def test_against_dictionary(self):
  3. c1 = Context()
  4. c1['update'] = 'value'
  5. self.assertEqual(c1.flatten(), {
  6. 'True': True,
  7. 'None': None,
  8. 'False': False,
  9. 'update': 'value',
  10. })

子类化Context:RequestContext

class RequestContext(request[, dict][, processors]_)

Django 带有一个特殊的Contextdjango.template.RequestContext,它与通常的django.template.Context 行为有少许不同。第一个不同点是,它接收一个HttpRequest 作为第一个参数。例如:

  1. c = RequestContext(request, {
  2. 'foo': 'bar',
  3. })

第二个不同点是,它根据引擎的 context_processors 配置选项自动向上下文中填充一些变量。

context_processors 选项是一个可调用对象 —— 叫做上下文处理器 —— 的列表,它们接收一个请求对象作为它们的参数并返回需要向上下文中添加的字典。在生成的默认设置文件中,默认的模板引擎包含下面几个上下文处理器:

  1. [
  2. 'django.template.context_processors.debug',
  3. 'django.template.context_processors.request',
  4. 'django.contrib.auth.context_processors.auth',
  5. 'django.contrib.messages.context_processors.messages',
  6. ]

Changed in Django 1.8:

Django 1.8 将模板内建的上下文处理器从django.core.context_processors 移动到django.template.context_processors中。

除此之外,RequestContext 始终启用'django.template.context_processors.csrf'。这是一个安全相关的上下文处理器,Admin 和其它Contrib 应用需要它,而且为了防止意外的错误配置,它被有意硬编码在其中且在context_processors 选项中不可以关闭。

每个处理器按顺序启用。这意味着,如果一个处理器向上下文添加一个变量,而第二个处理器添加一个相同名称的变量,第二个将覆盖第一个。默认的处理器会在下面解释。

上下文处理器应用的时机

上下文处理器应用在上下文数据的顶端。也就是说,上下文处理器可能覆盖你提供给ContextRequestContext 的变量,所以要注意避免与上下文处理器提供的变量名重复。

如果想要上下文数据的优先级高于上下文处理器,使用下面的模式:

  1. from django.template import RequestContext
  2. request_context = RequestContext(request)
  3. request_context.push({"my_name": "Adrian"})

Django 通过这种方式允许上下文数据在render()TemplateResponse 等API 中覆盖上下文处理器。

你还可以赋予RequestContext 一个额外的处理器列表,使用第三个可选的位置参数processors。在下面的示例中,RequestContext 实例获得一个ip_address 变量:

  1. from django.http import HttpResponse
  2. from django.template import RequestContext
  3. def ip_address_processor(request):
  4. return {'ip_address': request.META['REMOTE_ADDR']}
  5. def some_view(request):
  6. # ...
  7. c = RequestContext(request, {
  8. 'foo': 'bar',
  9. }, [ip_address_processor])
  10. return HttpResponse(t.render(c))

内建的模板上下文处理器

上下文处理器

下面是每个内置的上下文处理器所做的事情:

django.contrib.auth.context_processors.auth

如果启用这个处理器,每个RequestContext 将包含以下变量:

  • user – 一个 auth.User实例代表当前登录的用户 (或者 一个 AnonymousUser 实例, 如果用户没有登录).
  • perms – 一个 django.contrib.auth.context_processors.PermWrapper实例, 代表当前登录用户所拥有的权限.

django.template.context_processors.debug

如果开启这个处理器,每一个RequestContext将会包含两个变量—但是只有当你的?DEBUG配置设置为True时有效。请求的IP地址?(request.META[‘REMOTE_ADDR’]) is in the INTERNAL_IPS setting:

  • debugTrue. 你可以在模板中用它测试是否在DEBUG 模式。
  • sql_queries – ?一个{'sql': ..., 'time': ...} 字典的列表,表示请求期间到目前为止发生的每个SQL 查询及花费的时间。这个列表按查询的顺序排序,并直到访问时才生成。

django.template.context_processors.i18n

如果启用这个处理器,每个RequestContext 将包含两个变量:

  • LANGUAGESLANGUAGES 设置的值。
  • LANGUAGE_CODErequest.LANGUAGE_CODE, if it exists. 否则为LANGUAGE_CODE 设置的值。

更多信息,参见国际化和本地化

django.template.context_processors.media

如果启用这个处理器,每个RequestContext 将包含一个MEDIA_URL 变量,表示MEDIA_URL 设置的值。

django.template.context_processors.static

static()[source]

如果启用这个处理器,每个RequestContext 将包含一个STATIC_URL 变量,表示STATIC_URL 设置的值。

django.template.context_processors.csrf

上下文处理器添加一个token,这个token是?csrf_token 模版标签需要的,用来针对Cross Site Request Forgeries.

django.template.context_processors.request

如果启用这个处理器,每个RequestContext 将包含一个request 变量,表示当前的HttpRequest

django.contrib.messages.context_processors.messages

如果启用这个处理器,每个RequestContext 将包含下面两个变量:

  • messages – 通过消息框架设置的消息(字符串形式)列表。
  • DEFAULT_MESSAGE_LEVELS – 消息等级名称到它们数值 的映射。

Changed in Django 1.7:

添加DEFAULT_MESSAGE_LEVELS 变量。

编写你自己的上下文处理器

一个上下文处理器有一个非常简单的接口:它是一个参数的Python函数,这个参数是一个HttpRequest对象,并且返回一个字典,这个字典会被添加到模版上下文中。每个上下文处理器必须返回一个字典。

Custom context processors can live anywhere in your code base. All Django cares about is that your custom context processors are pointed to by the 'context_processors' option in your TEMPLATES setting — or the context_processors argument of Engine if you’re using it directly.

加载模板

通常情况下,你会将模板存储在文件系统上的文件中而不是自己使用底层的Template API。保存模板的目录叫做模板目录

Django 在许多地方查找模板目录,这取决于你的模板加载设置(参见下文中的“加载器类型”),但是指定模板目录最基本的方法是使用DIRS 选项。

DIRS

Changed in Django 1.8:

这个值以前通过TEMPLATE_DIRS 设置定义。

设置文件中TEMPLATES 设置的DIRS 选项或者Enginedirs 参数用于告诉Django 你的模板目录。它应该设置为一个字符串列表,包含模板目录的完整路径:

  1. TEMPLATES = [
  2. {
  3. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  4. 'DIRS': [
  5. '/home/html/templates/lawrence.com',
  6. '/home/html/templates/default',
  7. ],
  8. },
  9. ]

模板可以位于任何位置,只要Web 服务器可以读取这些目录和模板。它们可以具有任何扩展名例如.html.txt,或者完全没有扩展名。

注意,这些路径应该使用Unix 风格的前向斜杠,即使在Windows 上。

加载器类型

默认情况下,Django 使用基于文件系统的模板加载器,但是Django 自带几个其它的模板加载器,它们知道如何从其它源加载模板。

默认情况下,某些其它的加载器是被禁用的,但是你可以向TEMPLATES 设置中的DjangoTemplates 添加一个'loaders' 选项或者传递一个loaders 参数给Engine 来激活它们。loaders 应该为一个字符串列表或元组,每个字符串表示一个模板加载器类。下面是Django 自带的模板加载器:

django.template.loaders.filesystem.Loader

class filesystem.Loader

根据DIRS,从文件系统加载模板。

该加载器默认是启用的。当然,只有你将DIRS 设置为一个非空的列表,它才能找到模板:

  1. TEMPLATES = [{
  2. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  3. 'DIRS': [os.path.join(BASE_DIR, 'templates')],
  4. }]

django.template.loaders.app_directories.Loader

class app_directories.Loader

从文件系统加载Django 应用中的模板。对于INSTALLED_APPS 中的每个应用,该加载器会查找其下面的一个templates 子目录。如果该目录存在,Django 将在那里查找模板。

这意味着你可以将模板保存在每个单独的应用中。这还使得发布带有默认模板的Django 应用非常方便。

例如,对于这个设置:

  1. INSTALLED_APPS = ('myproject.polls', 'myproject.music')

get_template('foo.html') 将按顺序在下面的目录中查找foo.html

  • /path/to/myproject/polls/templates/
  • /path/to/myproject/music/templates/

…并将使用第一个找到的模板。

INSTALLED_APPS 的顺序非常重要!例如,如果你想自定义Django Admin,你可能选择使用myproject.polls 中自己的admin/base_site.html覆盖django.contrib.admin 中标准的admin/base_site.html 。那么你必须确保在INSTALLED_APPSmyproject.polls 位于django.contrib.admin之前,否则仍将加载 django.contrib.admin 中的模板并忽略你自己的模板。

注意,加载器在第一次运行时会做一些优化:它缓存一个含具有 templates 子目录的INSTALLED_APPS 包的列表。

你可以简单地通过设置APP_DIRSTrue 来启用这个加载器:

  1. TEMPLATES = [{
  2. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  3. 'APP_DIRS': True,
  4. }]

django.template.loaders.eggs.Loader

class eggs.Loader

与上面的app_directories 类似,只是它从Python eggs 中而不是从文件系统中加载模板。

这个加载器在默认情况下是禁用的。

django.template.loaders.cached.Loader

class cached.Loader

默认情况下,每当模版需要被渲染,模版系统将会读取和编译你的模版。但是,Django模版系统是非常高速的,?the overhead from reading and compiling?templates can add up.

基于缓存的模版加载器是一个基于类的加载器,that you configure with a list of other loaders?that it should wrap.The wrapped loaders are used to locate unknown templates when they are first encountered. 接下来,基于缓存加载器将编译过的Template存储在内存中。The cached Template instance is returned for subsequent requests to load the same template.

For example, to enable template caching with the filesystem and app_directories template loaders you might use the following settings:

  1. TEMPLATES = [{
  2. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  3. 'DIRS': [os.path.join(BASE_DIR, 'templates')],
  4. 'OPTIONS': {
  5. 'loaders': [
  6. ('django.template.loaders.cached.Loader', [
  7. 'django.template.loaders.filesystem.Loader',
  8. 'django.template.loaders.app_directories.Loader',
  9. ]),
  10. ],
  11. },
  12. }]

Note

All of the built-in Django template tags are safe to use with the cached loader, but if you’re using custom template tags that come from third party packages, or that you wrote yourself, you should ensure that the Node implementation for each tag is thread-safe. For more information, see template tag thread safety considerations.

这个加载器在默认情况下是禁用的。

django.template.loaders.locmem.Loader

New in Django 1.8.

class locmem.Loader

从一个Python 目录中加载模板。它主要用于测试。

这个加载器接收一个模板字典作为它的第一个参数:

  1. TEMPLATES = [{
  2. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  3. 'OPTIONS': {
  4. 'loaders': [
  5. ('django.template.loaders.locmem.Loader', {
  6. 'index.html': 'content here',
  7. }),
  8. ],
  9. },
  10. }]

这个加载器在默认情况下是禁用的。

Django 按照'loaders' 中选项的顺序使用模板加载器。它逐个使用每个加载器直到某个加载器找到一个匹配的模板。

自定义加载器

自定义加载器应该继承django.template.loaders.base.Loader 并覆盖load_template_source() 方法,这个方法接收一个template_name 参数、从磁盘(或其它地方)加载模板、然后返回一个元组:(template_string, template_origin)

Changed in Django 1.8:

django.template.loaders.base.Loader 以前定义在 django.template.loader.BaseLoader 中。

Loader类的load_template() 方法通过调用load_template_source() 获取模板字符串、从模板源中实例化一个Template、然后返回一个元组:(template, template_origin)

模板的origin 属性

New in Django 1.7.

Engine 使用debug=True 初始化时,它的模板将具有一个origin 属性,其值取决于模板加载的源。对于Django 初始化的引擎,debug 默认为DEBUG 设置的值。

class loader.LoaderOrigin

从模板加载器创建的模板将使用 django.template.loader.LoaderOrigin 类。

name

模板加载器返回的模板路径。对于从文件系统读取模板的加载器,它为模板的完整路径。

loadname

传递给模板加载器的模板相对路径。

class StringOrigin

Template 类创建的模板将使用django.template.StringOrigin 类。

source

用于创建模板的字符串。

{% endraw %}