Como implementar um backend de template personalizado

Backends personalizados

Aqui está como implementar um backend de template personalizado para utilizar um sistema de template. Um backend de template precisa herdar de django.template.backends.base.BaseEngine. Precisa implementar get_template() e opcionalmente from_string(). Aqui está um exemplo para a biblioteca de template fícticia foobar:

from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy

import foobar


class FooBar(BaseEngine):
    # Name of the subdirectory containing the templates for this engine
    # inside an installed application.
    app_dirname = "foobar"

    def __init__(self, params):
        params = params.copy()
        options = params.pop("OPTIONS").copy()
        super().__init__(params)

        self.engine = foobar.Engine(**options)

    def from_string(self, template_code):
        try:
            return Template(self.engine.from_string(template_code))
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)

    def get_template(self, template_name):
        try:
            return Template(self.engine.get_template(template_name))
        except foobar.TemplateNotFound as exc:
            raise TemplateDoesNotExist(exc.args, backend=self)
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)


class Template:
    def __init__(self, template):
        self.template = template

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        if request is not None:
            context["request"] = request
            context["csrf_input"] = csrf_input_lazy(request)
            context["csrf_token"] = csrf_token_lazy(request)
        return self.template.render(context)

Veja a DEP 182 para mais informações.

Debugar a integração para motores personalizados

A página de debug do Django tem hooks para prover informações detalhadas quando um erro de template é levantado. Motores de template customizados podem utilizar estes hooks para melhoras as informações de traceback que aparecem aos usuários. Os seguintes hooks estão disponíveis:

Template postmortem

O postmortem aparece quando TemplateDoesNotExist é levantado. Ele lista os motores de template e loaders que foram utilizados ao tentar encontrar o template informado. Por exemplo, se dois motores Django estão configurados, o postmortem será parecido com:

../_images/postmortem.png

Motores personalizados podem popular o postmortem por passar o backend e os argumentos tried ao levantar TemplateDoesNotExist. Backends que utilizam o postmortem devem especificar uma origem 1 ao objeto template.

Linha de informações contextuais

Se um erro acontece durante o tratamento ou renderização do template, o Django pode mostrar a linha em que o erro ocorreu. Por exemplo:

../_images/template-lines.png

Motores personalizados podem popular esta informação por definir um atributo template_debug nas exceções levantadas durante o tratamento e renderização. Esse atributo é um dict com os seguintes valores:

  • 'name': O nome do template onde a exceção ocorreu.

  • 'message': A mensagem de exceção.

  • 'source_lines': As linhas antes, depois, e incluindo a linha que exceção aconteceu. Isso é para contexto, por isso não teve mais do que cerca de 20 linhas.

  • 'line': O número da linha em que a exceção aconteceu.

  • 'before': O conteúdo na linha de erro anterior ao token que levantou o erro.

  • 'during': O token que levantou o erro.

  • 'after': O conteúdo da linha de erro posterior ao token que levantou o erro.

  • 'total': O número de linhas em source_lines.

  • 'top': O número da linha onde source_lines começa.

  • 'bottom': O número da linha onde source_lines termina.

Dado o seguinte erro de template, template_debug se parecerá com:

{
    "name": "/path/to/template.html",
    "message": "Invalid block tag: 'syntax'",
    "source_lines": [
        (1, "some\n"),
        (2, "lines\n"),
        (3, "before\n"),
        (4, "Hello {% syntax error %} {{ world }}\n"),
        (5, "some\n"),
        (6, "lines\n"),
        (7, "after\n"),
        (8, ""),
    ],
    "line": 4,
    "before": "Hello ",
    "during": "{% syntax error %}",
    "after": " {{ world }}\n",
    "total": 9,
    "bottom": 9,
    "top": 1,
}

API de Origem e integração com terceiros

Django templates have an Origin object available through the template.origin attribute. This enables debug information to be displayed in the template postmortem, as well as in 3rd-party libraries, like the Django Debug Toolbar.

Motores personalizados podem prover suas próprias informações de template.origin por criar um objeto que especifique os seguintes atributos:

  • 'name': O caminho completo do template.

  • 'template_name': O caminho relativo do template como foi passado aos mecanismos de carregamento de template.

  • 'loader_name': Uma string opcional identificando a função ou classe utilizada para carregar o template, por exemplo django.template.loaders.filesystem.Loader.