如何编写一个自定义的模板后端

自定义后端

以下是如何实现一个在另一个模板系统中使用的自定义后端。一个模板后端是继承自后端基本类 django.template.backends.base.BaseEngine 。它必须实现 get_template() 和可选实现 from_string() 。以下是一个模拟 foobar 模板库的例子。

  1. from django.template import TemplateDoesNotExist, TemplateSyntaxError
  2. from django.template.backends.base import BaseEngine
  3. from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
  4. import foobar
  5. class FooBar(BaseEngine):
  6. # Name of the subdirectory containing the templates for this engine
  7. # inside an installed application.
  8. app_dirname = 'foobar'
  9. def __init__(self, params):
  10. params = params.copy()
  11. options = params.pop('OPTIONS').copy()
  12. super().__init__(params)
  13. self.engine = foobar.Engine(**options)
  14. def from_string(self, template_code):
  15. try:
  16. return Template(self.engine.from_string(template_code))
  17. except foobar.TemplateCompilationFailed as exc:
  18. raise TemplateSyntaxError(exc.args)
  19. def get_template(self, template_name):
  20. try:
  21. return Template(self.engine.get_template(template_name))
  22. except foobar.TemplateNotFound as exc:
  23. raise TemplateDoesNotExist(exc.args, backend=self)
  24. except foobar.TemplateCompilationFailed as exc:
  25. raise TemplateSyntaxError(exc.args)
  26. class Template:
  27. def __init__(self, template):
  28. self.template = template
  29. def render(self, context=None, request=None):
  30. if context is None:
  31. context = {}
  32. if request is not None:
  33. context['request'] = request
  34. context['csrf_input'] = csrf_input_lazy(request)
  35. context['csrf_token'] = csrf_token_lazy(request)
  36. return self.template.render(context)

请参阅 DEP 182 以获取更多信息。

为自定义引擎集成调试功能

当模板有错误时Django调试页面会提供相应的钩子信息。自定义后端引擎能使用这些钩子来细化显示给用户的回溯信息。以下是可用的钩子:

模板剖析

当错误 TemplateDoesNotExist 发生时显示剖析。它会列举出尝试查找指定模板时使用的模板引擎和加载器。举个例子,如果配置了两个Django引擎,剖析显示如下:

../../_images/postmortem.png

TemplateDoesNotExist 错误触发时自定义引擎会填写 后端尝试 参数。使用剖析 :ref:` 的后端必须要指定模板对象上的一个来源 1` 。

上下文信息

当模板解析或渲染时发生错误,Django会显示错误所在的行。举个例子:

../../_images/template-lines.png

在解析或渲染异常中配置了 template_debug 属性的自定义引擎会显示这条信息。这个属性是一个有以下值的类 dict

  • 'name' :发生异常的模板名称
  • 'message': 异常信息。
  • 'source_lines': 异常发生的行及其前后内容。这是为了上下文,所以它不应该超过二十行。
  • 'line': 异常发生的行数。
  • 'before': 发生错误的标识符的错误行前面的内容。
  • 'during': 发生错误的标识符。
  • 'after': 发生错误的标识符的错误行后面的内容。
  • 'total': source_lines 总行数。
  • 'top': source_lines 起始行数。
  • 'bottom': source_lines 结束的行数。

根据上述模板错误, template_debug 会像这样:

  1. {
  2. 'name': '/path/to/template.html',
  3. 'message': "Invalid block tag: 'syntax'",
  4. 'source_lines': [
  5. (1, 'some\n'),
  6. (2, 'lines\n'),
  7. (3, 'before\n'),
  8. (4, 'Hello {% syntax error %} {{ world }}\n'),
  9. (5, 'some\n'),
  10. (6, 'lines\n'),
  11. (7, 'after\n'),
  12. (8, ''),
  13. ],
  14. 'line': 4,
  15. 'before': 'Hello ',
  16. 'during': '{% syntax error %}',
  17. 'after': ' {{ world }}\n',
  18. 'total': 9,
  19. 'bottom': 9,
  20. 'top': 1,
  21. }

原始API和第3方集成

Django有一个可用 template.origin 属性的 Origin 基本对象类。这可以让调试信息显示在 :ref:`template 模板剖析上,同时支持第3方库,例如 `Django Debug Toolbar`_

自定义引擎可以通过创建有以下特定属性的对象来提供自身的 template.origin 信息。

  • 'name': 模板的完整路径。
  • 'template_name': 通过模板加载方法打开的模板的相对路径。
  • 'loader_name': 一个可选的以字符串形式指定用来加载模板的文件系统类或函数, e.g. django.template.loaders.filesystem.Loader.