custom-template-backend.txt 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. ==========================================
  2. How to implement a custom template backend
  3. ==========================================
  4. Custom backends
  5. ---------------
  6. Here's how to implement a custom template backend in order to use another
  7. template system. A template backend is a class that inherits
  8. ``django.template.backends.base.BaseEngine``. It must implement
  9. ``get_template()`` and optionally ``from_string()``. Here's an example for a
  10. fictional ``foobar`` template library::
  11. from django.template import TemplateDoesNotExist, TemplateSyntaxError
  12. from django.template.backends.base import BaseEngine
  13. from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
  14. import foobar
  15. class FooBar(BaseEngine):
  16. # Name of the subdirectory containing the templates for this engine
  17. # inside an installed application.
  18. app_dirname = "foobar"
  19. def __init__(self, params):
  20. params = params.copy()
  21. options = params.pop("OPTIONS").copy()
  22. super().__init__(params)
  23. self.engine = foobar.Engine(**options)
  24. def from_string(self, template_code):
  25. try:
  26. return Template(self.engine.from_string(template_code))
  27. except foobar.TemplateCompilationFailed as exc:
  28. raise TemplateSyntaxError(exc.args)
  29. def get_template(self, template_name):
  30. try:
  31. return Template(self.engine.get_template(template_name))
  32. except foobar.TemplateNotFound as exc:
  33. raise TemplateDoesNotExist(exc.args, backend=self)
  34. except foobar.TemplateCompilationFailed as exc:
  35. raise TemplateSyntaxError(exc.args)
  36. class Template:
  37. def __init__(self, template):
  38. self.template = template
  39. def render(self, context=None, request=None):
  40. if context is None:
  41. context = {}
  42. if request is not None:
  43. context["request"] = request
  44. context["csrf_input"] = csrf_input_lazy(request)
  45. context["csrf_token"] = csrf_token_lazy(request)
  46. return self.template.render(context)
  47. See `DEP 182`_ for more information.
  48. .. _template-debug-integration:
  49. Debug integration for custom engines
  50. ------------------------------------
  51. The Django debug page has hooks to provide detailed information when a template
  52. error arises. Custom template engines can use these hooks to enhance the
  53. traceback information that appears to users. The following hooks are available:
  54. .. _template-postmortem:
  55. Template postmortem
  56. ~~~~~~~~~~~~~~~~~~~
  57. The postmortem appears when :exc:`~django.template.TemplateDoesNotExist` is
  58. raised. It lists the template engines and loaders that were used when trying to
  59. find a given template. For example, if two Django engines are configured, the
  60. postmortem will appear like:
  61. .. image:: _images/postmortem.png
  62. Custom engines can populate the postmortem by passing the ``backend`` and
  63. ``tried`` arguments when raising :exc:`~django.template.TemplateDoesNotExist`.
  64. Backends that use the postmortem :ref:`should specify an origin
  65. <template-origin-api>` on the template object.
  66. Contextual line information
  67. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  68. If an error happens during template parsing or rendering, Django can display
  69. the line the error happened on. For example:
  70. .. image:: _images/template-lines.png
  71. Custom engines can populate this information by setting a ``template_debug``
  72. attribute on exceptions raised during parsing and rendering. This attribute is
  73. a :class:`dict` with the following values:
  74. * ``'name'``: The name of the template in which the exception occurred.
  75. * ``'message'``: The exception message.
  76. * ``'source_lines'``: The lines before, after, and including the line the
  77. exception occurred on. This is for context, so it shouldn't contain more than
  78. 20 lines or so.
  79. * ``'line'``: The line number on which the exception occurred.
  80. * ``'before'``: The content on the error line before the token that raised the
  81. error.
  82. * ``'during'``: The token that raised the error.
  83. * ``'after'``: The content on the error line after the token that raised the
  84. error.
  85. * ``'total'``: The number of lines in ``source_lines``.
  86. * ``'top'``: The line number where ``source_lines`` starts.
  87. * ``'bottom'``: The line number where ``source_lines`` ends.
  88. Given the above template error, ``template_debug`` would look like::
  89. {
  90. "name": "/path/to/template.html",
  91. "message": "Invalid block tag: 'syntax'",
  92. "source_lines": [
  93. (1, "some\n"),
  94. (2, "lines\n"),
  95. (3, "before\n"),
  96. (4, "Hello {% syntax error %} {{ world }}\n"),
  97. (5, "some\n"),
  98. (6, "lines\n"),
  99. (7, "after\n"),
  100. (8, ""),
  101. ],
  102. "line": 4,
  103. "before": "Hello ",
  104. "during": "{% syntax error %}",
  105. "after": " {{ world }}\n",
  106. "total": 9,
  107. "bottom": 9,
  108. "top": 1,
  109. }
  110. .. _template-origin-api:
  111. Origin API and 3rd-party integration
  112. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  113. Django templates have an :class:`~django.template.base.Origin` object available
  114. through the ``template.origin`` attribute. This enables debug information to be
  115. displayed in the :ref:`template postmortem <template-postmortem>`, as well as
  116. in 3rd-party libraries, like the :pypi:`Django Debug Toolbar
  117. <django-debug-toolbar>`.
  118. Custom engines can provide their own ``template.origin`` information by
  119. creating an object that specifies the following attributes:
  120. * ``'name'``: The full path to the template.
  121. * ``'template_name'``: The relative path to the template as passed into the
  122. template loading methods.
  123. * ``'loader_name'``: An optional string identifying the function or class used
  124. to load the template, e.g. ``django.template.loaders.filesystem.Loader``.
  125. .. _DEP 182: https://github.com/django/deps/blob/main/final/0182-multiple-template-engines.rst