El lenguaje de plantillas Django: para programadores de Python

Este documento explica el sistema de plantillas de Django desde una perspectiva técnica – cómo funciona y cómo extenderlo. Si estás buscando referencias sobre la sintaxis del lenguaje, consulta El lenguaje de plantillas de Django.

Supone una comprensión de plantillas, contextos, variables, etiquetas y renderizado. Comienza con la introducción al lenguaje de plantillas Django si no estás familiarizado con estos conceptos.

Resumen

El uso del sistema de plantillas en Python es un proceso de tres pasos:

  1. Configuras una Motor.

  2. Compilas el código de la plantilla en una Plantilla.

  3. Renduces la plantilla con un Contexto.

Los proyectos Django suelen depender de las API de alto nivel y agnósticas a la parte trasera <template-engines> para cada uno de estos pasos en lugar de las API de bajo nivel del sistema de plantillas:

  1. Para cada backend de plantilla DjangoTemplates en la configuración TEMPLATES, Django instancia un Motor. DjangoTemplates envuelve el Motor y lo adapta a la API común del backend de plantillas.

  2. El módulo django.template.loader proporciona funciones como get_template() para cargar plantillas. Devuelven una django.template.backends.django.Template que envuelve la verdadera django.template.Template.

  3. La Plantilla obtenida en el paso anterior tiene un método render() que marshala un contexto y posiblemente una solicitud a un Contexto y delega el renderizado al Template subyacente.

Configuración de un motor

Si estás utilizando el backend de plantillas DjangoTemplates, probablemente no es la documentación que estás buscando. Una instancia de la clase Engine descrita a continuación está accesible mediante el atributo engine de ese backend y cualquier valor por defecto mencionado a continuación se sobreescribe por lo que se pasa en DjangoTemplates.

class Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True)[fuente]

Al instanciar un Engine, todos los argumentos deben pasar como argumentos clave:

  • dirs es una lista de directorios donde el motor debe buscar archivos de origen de plantillas. Se utiliza para configurar filesystem.Loader.

    Por defecto, es una lista vacía.

  • app_dirs solo afecta al valor por defecto de loaders. Consulte a continuación.

    Por defecto, es False.

  • autoescape controla si se habilita la autoescapada HTML.

    Por defecto, es True.

    Advertencia

    Solo establezca en False si estás renderizando plantillas no-HTML!

  • context_processors es una lista de caminos de Python a punto con llamables que se utilizan para poblar el contexto cuando se renderiza un template con una solicitud. Estos llamables toman un objeto de solicitud como argumento y devuelven un dict de elementos para ser fusionados en el contexto.

    Por defecto, es una lista vacía.

    Ver RequestContext para obtener más información.

  • debug es un valor booleano que activa/desactiva el modo de depuración de plantillas. Si es True, el motor de plantillas almacenará información de depuración adicional que se puede utilizar para mostrar un informe detallado sobre cualquier excepción levantada durante la renderización de plantillas.

    Por defecto, es False.

  • loaders es una lista de clases de cargadores de plantillas, especificadas como cadenas de texto. Cada clase Loader sabe cómo importar plantillas desde una fuente particular. Opcionalmente, se puede utilizar un tupla en lugar de una cadena de texto. El primer elemento de la tupla debe ser el nombre de la clase Loader, los elementos subsiguientes se pasan a la clase Loader durante su inicialización.

    Por defecto, contiene una lista con:

    • “django.template.loaders.filesystem.Loader”

    • Si y solo si app_dirs es True.

    Estos cargadores luego se envuelven en django.template.loaders.cached.Loader.

    Ver cargadores-de-plantillas para detalles.

  • La salida, como cadena de caracteres, que el sistema de plantillas debe utilizar para variables inválidas (por ejemplo, con errores de ortografía).

    Por defecto, es la cadena vacía.

    See variables-de-plantilla-invalidas para detalles.

  • file_charset es el conjunto de caracteres utilizado para leer archivos de plantillas en disco.

    Por defecto, se utiliza 'utf-8'.

  • 'libraries': Un diccionario de etiquetas y rutas de Python puntuadas de módulos de etiquetas de plantilla para registrar con el motor de plantillas. Se utiliza para agregar nuevas bibliotecas o proporcionar etiquetas alternativas para las existentes. Por ejemplo:

    Engine(
        libraries={
            "myapp_tags": "path.to.myapp.tags",
            "admin.urls": "django.contrib.admin.templatetags.admin_urls",
        },
    )
    

    Las bibliotecas se pueden cargar pasando la clave correspondiente del diccionario al tag {% load %}.

  • 'builtins': Una lista de rutas de Python puntuadas de módulos de etiquetas de plantilla para agregar a integraciones. Por ejemplo:

    Engine(
        builtins=["myapp.builtins"],
    )
    

    Las etiquetas y filtros de las bibliotecas integradas se pueden utilizar sin llamar previamente al tag {% load %}.

static Engine.get_default()[fuente]

Devuelve el motor subyacente Engine del primer motor configurado DjangoTemplates. Lanza ImproperlyConfigured si no se han configurado motores.

Es necesario para preservar APIs que dependen de un motor globalmente disponible e implícitamente configurado. Cualquier otro uso se desaconseja fuertemente.

Engine.from_string(template_code)[fuente]

Compila el código de plantilla dado y devuelve un objeto Template.

Engine.get_template(template_name)[fuente]

Carga un template con el nombre dado, lo compila y devuelve un objeto Template.

Engine.select_template(template_name_list)[fuente]

Al igual que get_template(), excepto que acepta una lista de nombres y devuelve el primer template encontrado.

La carga de un template

La forma recomendada de crear un Template es llamando a los métodos de fábrica del motor de plantillas: get_template(), select_template() y from_string().

En un proyecto Django donde la configuración TEMPLATES define un motor de plantillas DjangoTemplates, es posible instanciar directamente un objeto Template. Si se definen más de una configuración DjangoTemplates, se utilizará la primera.

class Template[fuente]

Esta clase vive en django.template.Template. El constructor acepta un argumento — el código de plantilla bruto:

from django.template import Template

template = Template("My name is {{ my_name }}.")

Detrás de escena

El sistema solo parsea tu código de plantilla bruto una vez – cuando creas el objeto Template. A partir de entonces, se almacena internamente como estructura de árbol para mejorar el rendimiento.

Incluso la propia parsificación es bastante rápida. La mayoría de la parsificación sucede mediante un solo llamado a una expresión regular corta y simple.

Rendición de contexto

Una vez que tengas un objeto compilado Template, puedes renderizar un contexto con él. Puedes reutilizar el mismo plantilla para renderizarla varias veces con diferentes contextos.

class Context(dict_=None, autoescape=True, use_l10n=None, use_tz=None)[fuente]

El constructor de django.template.Context toma un argumento opcional — un diccionario que mapea nombres de variables a valores de variable.

Tres argumentos de palabra clave opcionales también se pueden especificar:

Para ver un ejemplo de uso, consulte Jugando con objetos Context a continuación.

Template.render(context)[fuente]

Llama al método render() del objeto Template con un Context para «rellenar» la plantilla:

>>> from django.template import Context, Template
>>> template = Template("My name is {{ my_name }}.")

>>> context = Context({"my_name": "Adrian"})
>>> template.render(context)
"My name is Adrian."

>>> context = Context({"my_name": "Dolores"})
>>> template.render(context)
"My name is Dolores."

Variables y consultas

Los nombres de las variables deben consistir en cualquier letra (A-Z), cualquier dígito (0-9), un guión bajo (pero no pueden comenzar con un guión bajo) o un punto.

Los puntos tienen un significado especial en la renderización de plantillas. Un punto en el nombre de una variable significa una consulta. Específicamente, cuando el sistema de plantillas encuentra un punto en el nombre de una variable, intenta las siguientes consultas, en este orden:

  • Consulta de diccionario. Ejemplo: foo["bar"]

  • Consulta de atributo. Ejemplo: foo.bar

  • Consulta de índice de lista. Ejemplo: foo[bar]

Ten en cuenta que «bar» en una expresión de plantilla como {{ foo.bar }} se interpretará como una cadena literal y no utilizará el valor de la variable «bar», si existe en el contexto de la plantilla.

El sistema de plantillas utiliza el primer tipo de consulta que funciona. Es lógica cortocircuito. Aquí hay algunos ejemplos:

>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."

>>> class PersonClass:
...     pass
...
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."

>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."

Si cualquier parte de la variable es llamable, el sistema de plantillas intentará llamarla. Ejemplo:

>>> class PersonClass2:
...     def name(self):
...         return "Samantha"
...
>>> t = Template("My name is {{ person.name }}.")
>>> t.render(Context({"person": PersonClass2}))
"My name is Samantha."

Las variables llamables son ligeramente más complejas que las variables que solo requieren consultas directas. Aquí hay algunas cosas en las que debes tener en cuenta:

  • Si la variable lanza una excepción cuando se llama, la excepción será propagada a menos que la excepción tenga un atributo silent_variable_failure cuyo valor sea True. Si la excepción tiene un atributo silent_variable_failure cuyo valor es True, la variable se renderizará con el valor de la opción de configuración del motor string_if_invalid (una cadena vacía, por defecto). Ejemplo:

    >>> t = Template("My name is {{ person.first_name }}.")
    >>> class PersonClass3:
    ...     def first_name(self):
    ...         raise AssertionError("foo")
    ...
    >>> p = PersonClass3()
    >>> t.render(Context({"person": p}))
    Traceback (most recent call last):
    ...
    AssertionError: foo
    
    >>> class SilentAssertionError(Exception):
    ...     silent_variable_failure = True
    ...
    >>> class PersonClass4:
    ...     def first_name(self):
    ...         raise SilentAssertionError
    ...
    >>> p = PersonClass4()
    >>> t.render(Context({"person": p}))
    "My name is ."
    

    Ten en cuenta que django.core.exceptions.ObjectDoesNotExist, que es la clase base para todas las excepciones DoesNotExist de la API de bases de datos de Django, tiene silent_variable_failure = True. Por lo tanto, si estás utilizando plantillas de Django con objetos de modelo de Django, cualquier excepción DoesNotExist fallará silenciosamente.

  • Una variable solo se puede llamar si no tiene argumentos requeridos. De lo contrario, el sistema devolverá el valor de la opción de configuración del motor string_if_invalid.

  • Puede haber efectos laterales cuando se llama a algunas variables, y sería tanto tonto como un agujero de seguridad permitir que el sistema de plantillas tenga acceso a ellos.

    Un buen ejemplo es el método delete() en cada objeto de modelo de Django. El sistema de plantillas no debería poder hacer algo así:

    I will now delete this valuable data. {{ data.delete }}
    

    Para evitar esto, establece un atributo alters_data en la variable llamable. El sistema de plantillas no llamará a una variable si tiene alters_data=True configurado y, en su lugar, reemplazará la variable con string_if_invalid, sin condiciones. Los métodos dinámicamente generados delete() y save() en los objetos de modelo de Django obtienen alters_data=True automáticamente. Ejemplo:

    def sensitive_function(self):
        self.database_record.delete()
    
    
    sensitive_function.alters_data = True
    
  • Ocasionalmente, puede desear desactivar esta característica por otras razones y decirle al sistema de plantillas que deje una variable sin llamar independientemente de lo que sea. Para hacerlo, establece un atributo do_not_call_in_templates en el llamable con el valor True. El sistema de plantillas entonces actuará como si tu variable no fuera llamable (por ejemplo, permitiendo acceder a los atributos del llamable).

Cómo se manejan las variables inválidas

Generalmente, si una variable no existe, el sistema de plantillas inserta el valor de la opción de configuración del motor string_if_invalid, que está establecido en '' (la cadena vacía) por defecto.

Los filtros aplicados a una variable inválida solo se aplicarán si string_if_invalid está establecido en '' (la cadena vacía). Si string_if_invalid está establecido en cualquier otro valor, los filtros de variables serán ignorados.

Este comportamiento es ligeramente diferente para las etiquetas de plantilla if, for y regroup. Si se proporciona una variable inválida a alguna de estas etiquetas de plantilla, la variable se interpretará como None. Los filtros siempre se aplican a variables inválidas dentro de estas etiquetas de plantilla.

Si string_if_invalid contiene un marcador de formato '%s', el marcador de formato se reemplazará con el nombre de la variable inválida.

Solo para fines de depuración!

Aunque string_if_invalid puede ser una herramienta útil para la depuración, es una mala idea habilitarlo como “predeterminado de desarrollo”.

Muchas plantillas, incluidas algunas de Django, dependen del silencio del sistema de plantilla cuando se encuentra una variable inexistente. Si asignas un valor distinto a '' a string_if_invalid, experimentarás problemas de renderizado con estas plantillas y sitios.

En general, string_if_invalid solo debe habilitarse para depurar un problema específico de la plantilla, y luego deshabilitarlo una vez que se complete la depuración.

Variables integradas

Cada contexto contiene True, False y None. Como esperarías, estas variables resuelven a los objetos Python correspondientes.

Limitaciones con literales de cadena

El lenguaje de plantilla de Django no tiene forma de escapar los caracteres utilizados para su propia sintaxis. Por ejemplo, la etiqueta de plantilla templatetag es necesaria si necesitas output secuencias de caracteres como {% y %}.

A un problema similar existe si deseas incluir estas secuencias en argumentos de filtros o etiquetas de plantilla. Por ejemplo, cuando se parsea una etiqueta bloque, el parser de plantillas de Django busca la primera ocurrencia de %} después de una {%. Esto impide el uso de "%}" como literal de cadena. Por ejemplo, se levantará un error TemplateSyntaxError para las siguientes expresiones:

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

{% with tvar="Some string literal with %} in it." %}{% endwith %}

El mismo problema puede ser desencadenado por el uso de una secuencia reservada en argumentos de filtro:

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

Si necesitas usar cadenas con estas secuencias, almacénalas en variables de plantilla o utiliza un etiqueta o filtro personalizado para superar la limitación.

Jugando con objetos Context

La mayoría del tiempo, instancias los objetos Context pasándole un diccionario completamente poblado a Context(). Pero también puedes agregar y eliminar elementos de un objeto Context una vez que ha sido instanciado, utilizando la sintaxis estándar de diccionarios:

>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c["foo"]
'bar'
>>> del c["foo"]
>>> c["foo"]
Traceback (most recent call last):
...
KeyError: 'foo'
>>> c["newvariable"] = "hello"
>>> c["newvariable"]
'hello'
Context.get(key, otherwise=None)

Devuelve el valor para key si key está en el contexto, o devuelve otherwise.

Context.setdefault(key, default=None)

Si key está en el contexto, devuelve su valor. De lo contrario, inserta key con un valor de default y devuelve default.

Context.pop()
Context.push()
exception ContextPopException[fuente]

Un objeto Context es una pila. Es decir, puedes push() y pop(). Si pop() demasiado, levantará un django.template.ContextPopException:

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.push()
{}
>>> c["foo"] = "second level"
>>> c["foo"]
'second level'
>>> c.pop()
{'foo': 'second level'}
>>> c["foo"]
'first level'
>>> c["foo"] = "overwritten"
>>> c["foo"]
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
ContextPopException

También puedes utilizar push() como administrador de contexto para asegurarte de que se llama una pop() correspondiente.

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.push():
...     c["foo"] = "second level"
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'

Todos los argumentos pasados a push() se pasarán al constructor dict utilizado para construir el nuevo nivel del contexto.

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.push(foo="second level"):
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'
Context.update(other_dict)[fuente]

Además de push() y pop(), el objeto Context también define un método update(). Este funciona como push() pero toma un diccionario como argumento y empuja ese diccionario en lugar de uno vacío sobre la pila.

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.update({"foo": "updated"})
{'foo': 'updated'}
>>> c["foo"]
'updated'
>>> c.pop()
{'foo': 'updated'}
>>> c["foo"]
'first level'

Al igual que push(), puedes utilizar update() como administrador de contexto para asegurarte de que se llama un pop() correspondiente.

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.update({"foo": "second level"}):
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'

Usar un Context como una pila resulta útil en algunas etiquetas de plantilla personalizadas.

Context.flatten()

El método flatten() te permite obtener toda la pila del objeto Context como un solo diccionario, incluyendo variables incorporadas.

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.update({"bar": "second level"})
{'bar': 'second level'}
>>> c.flatten()
{'True': True, 'None': None, 'foo': 'first level', 'False': False, 'bar': 'second level'}

También se utiliza internamente el método flatten() para hacer que los objetos Context sean comparables.

>>> c1 = Context()
>>> c1["foo"] = "first level"
>>> c1["bar"] = "second level"
>>> c2 = Context()
>>> c2.update({"bar": "second level", "foo": "first level"})
{'foo': 'first level', 'bar': 'second level'}
>>> c1 == c2
True

El resultado de flatten() puede ser útil en pruebas unitarias para comparar Context con dict:

class ContextTest(unittest.TestCase):
    def test_against_dictionary(self):
        c1 = Context()
        c1["update"] = "value"
        self.assertEqual(
            c1.flatten(),
            {
                "True": True,
                "None": None,
                "False": False,
                "update": "value",
            },
        )

Usando RequestContext

class RequestContext(request, dict_=None, processors=None, use_l10n=None, use_tz=None, autoescape=True)[fuente]

Django viene con una clase especial Context llamada django.template.RequestContext, que actúa ligeramente diferente de la normal django.template.Context. La primera diferencia es que toma un objeto HttpRequest como su primer argumento. Por ejemplo:

c = RequestContext(
    request,
    {
        "foo": "bar",
    },
)

La segunda diferencia es que automáticamente poblado el contexto con algunas variables, según la configuración del motor de plantillas context_processors.

La opción context_processors es una lista de llamables – llamados procesadores de contexto – que toman un objeto de solicitud como argumento y devuelven un diccionario de elementos para fusionar con el contexto. En la archivo de configuración generado por defecto, el motor de plantillas predeterminado contiene los siguientes procesadores de contexto:

[
    "django.template.context_processors.request",
    "django.contrib.auth.context_processors.auth",
    "django.contrib.messages.context_processors.messages",
]

Además de estos, RequestContext siempre habilita 'django.template.context_processors.csrf'. Esto es un procesador de contexto relacionado con la seguridad que se requiere por los apps contrib y el admin, y en caso de configuración accidental, está codificado deliberadamente y no puede ser deshabilitado en la opción context_processors.

Cada procesador se aplica en orden. Esto significa que si un procesador agrega una variable al contexto y un segundo procesador agrega una variable con el mismo nombre, el segundo sobreescribirá al primero. Los procesadores por defecto se explican a continuación.

Cuándo se aplican los procesadores de contexto

Los procesadores de contexto se aplican encima de los datos del contexto. Esto significa que un procesador de contexto puede sobreescribir variables que has suministrado a tu Context o RequestContext, así que ten cuidado para evitar nombres de variable que coincidan con aquellos proporcionados por tus procesadores de contexto.

Si deseas que los datos del contexto tengan prioridad sobre los procesadores de contexto, utiliza el siguiente patrón:

from django.template import RequestContext

request_context = RequestContext(request)
request_context.push({"my_name": "Adrian"})

Django hace esto para permitir que los datos del contexto sobreescriban a los procesadores de contexto en APIs como render() y TemplateResponse.

También puedes darle a RequestContext una lista de procesadores adicionales, utilizando el tercer argumento posicional opcional processors. En este ejemplo, la instancia de RequestContext obtiene una variable ip_address.

from django.http import HttpResponse
from django.template import RequestContext, Template


def ip_address_processor(request):
    return {"ip_address": request.META["REMOTE_ADDR"]}


def client_ip_view(request):
    template = Template("{{ title }}: {{ ip_address }}")
    context = RequestContext(
        request,
        {
            "title": "Your IP Address",
        },
        [ip_address_processor],
    )
    return HttpResponse(template.render(context))

Procesadores de contexto de plantilla integrados

Aquí está lo que hace cada uno de los procesadores integrados:

django.contrib.auth.context_processors.auth

auth(request)[fuente]

If este procesador está habilitado, cada RequestContext contendrá estas variables:

  • user – Una instancia de auth.User que representa al usuario actualmente conectado (o una instancia de AnonymousUser, si el cliente no está conectado).

  • perms – Una instancia de django.contrib.auth.context_processors.PermWrapper, que representa los permisos que tiene el usuario actualmente conectado.

django.template.context_processors.debug

debug(request)[fuente]

Si este procesador está habilitado, cada RequestContext contendrá estas dos variables – pero solo si se establece tu configuración DEBUG en True y la dirección IP de la solicitud (request.META['REMOTE_ADDR']) está en la configuración INTERNAL_IPS:

  • debugTrue. Puedes utilizar esto en plantillas para comprobar si estás en modo DEBUG.

  • sql_queries – Una lista de diccionarios {'sql': ..., 'time': ...}, que representan cada consulta SQL que ha ocurrido hasta el momento durante la solicitud y cuánto tiempo llevó. La lista se genera con pereza al acceder a ella.

django.template.context_processors.i18n

i18n(request)[fuente]

If este procesador está habilitado, cada RequestContext contendrá estas variables:

  • LANGUAGES – El valor de la configuración LANGUAGES.

  • LANGUAGE_BIDITrue si el idioma actual es un idioma de derecha a izquierda, por ejemplo hebreo, árabe. False si es un idioma de izquierda a derecha, por ejemplo inglés, francés, alemán.

  • LANGUAGE_CODErequest.LANGUAGE_CODE, si existe. De lo contrario, el valor de la configuración de LANGUAGE_CODE.

Vea etiquetas de plantilla i18n para obtener información sobre las etiquetas de plantilla que generan los mismos valores.

django.template.context_processors.media

Si se habilita este procesador, cada RequestContext contendrá una variable MEDIA_URL, proporcionando el valor de la configuración de MEDIA_URL.

django.template.context_processors.static

static(request)[fuente]

Si se habilita este procesador, cada RequestContext contendrá una variable STATIC_URL, proporcionando el valor de la configuración de STATIC_URL.

django.template.context_processors.csrf

Este procesador agrega un token que es necesario para la etiqueta de plantilla csrf_token para proteger contra Falsificación de Solicitudes de Sitio Cruzado.

django.template.context_processors.request

Si se habilita este procesador, cada RequestContext contendrá una variable request, que es la solicitud actual HttpRequest.

django.template.context_processors.tz

tz(request)[fuente]

Si este procesador está habilitado, cada contexto de solicitud RequestContext contendrá una variable TIME_ZONE, que proporciona el nombre del zona horaria actualmente activa.

django.contrib.messages.context_processors.messages

Si este procesador está habilitado, cada contexto de solicitud RequestContext contendrá estas dos variables:

  • messages – Una lista de mensajes (como cadenas) que se han establecido a través del framework de mensajes.

  • DEFAULT_MESSAGE_LEVELS – Un mapeo de los nombres de nivel de mensaje a su valor numérico correspondiente <message-level-constants>.

Escribiendo tus propios procesadores de contexto

Un procesador de contexto tiene una interfaz simple: Es una función de Python que toma un argumento, un objeto HttpRequest, y devuelve un diccionario que se agrega al contexto del template.

Por ejemplo, para agregar el valor de la configuración DEFAULT_FROM_EMAIL a cada contexto:

from django.conf import settings


def from_email(request):
    return {
        "DEFAULT_FROM_EMAIL": settings.DEFAULT_FROM_EMAIL,
    }

Los procesadores de contexto personalizados pueden vivir en cualquier parte de tu base de código. Lo único que importa es que tus procesadores de contexto personalizados estén apuntados por la opción 'context_processors' en tu configuración TEMPLATES — o el argumento context_processors de la clase Engine si lo estás utilizando directamente.

Carga de plantillas

En general, almacenarás las plantillas en archivos en tu sistema de archivos en lugar de utilizar la API de nivel bajo Template tú mismo. Almacena las plantillas en un directorio especificado como directorio de plantillas.

Django busca los directorios de plantillas en varios lugares, dependiendo de tus configuraciones de carga de plantillas (consulte «Tipos de cargadores» a continuación), pero la forma más básica de especificar los directorios de plantillas es utilizando la opción DIRS.

La opción DIRS

Dile a Django qué son tus directorios de plantillas utilizando la opción DIRS en la configuración TEMPLATES en tu archivo de configuración — o el argumento dirs de Engine. Esto debe estar configurado para una lista de cadenas que contengan los caminos completos a tus directorios de plantillas:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            "/home/html/templates/lawrence.com",
            "/home/html/templates/default",
        ],
    },
]

Las plantillas pueden ir donde tú quieras, siempre y cuando los directorios y las plantillas sean legibles por el servidor web. Pueden tener cualquier extensión que desees, como .html o .txt, o no tener ninguna.

Ten en cuenta que estos caminos deben utilizar forward slashes estilo Unix, incluso en Windows.

Tipos de cargadores

Por defecto, Django utiliza un cargador de plantillas basado en el sistema de archivos, pero Django viene con unos pocos otros cargadores de plantillas que saben cómo cargar las plantillas desde otras fuentes.

Algunos de estos otros cargadores están desactivados por defecto, pero puedes activarlos agregando una opción 'loaders' a tu backend DjangoTemplates en la configuración TEMPLATES o pasando un argumento loaders a Engine. loaders debe ser una lista de cadenas o tuplas, donde cada uno representa una clase del cargador de plantillas. Aquí están los cargadores de plantillas que vienen con Django:

django.template.loaders.filesystem.Loader

class filesystem.Loader

Carga plantillas desde el sistema de archivos, según DIRS.

Este cargador está habilitado por defecto. Sin embargo, no encontrará ninguna plantilla hasta que establezca DIRS en una lista no vacía:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
    }
]

También puede sobrescribir 'DIRS' y especificar directorios específicos para un cargador de sistema de archivos particular:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.filesystem.Loader",
                    [BASE_DIR / "templates"],
                ),
            ],
        },
    }
]

django.template.loaders.app_directories.Loader

class app_directories.Loader

Carga plantillas desde aplicaciones Django en el sistema de archivos. Para cada aplicación en INSTALLED_APPS, el cargador busca una subcarpeta templates. Si la carpeta existe, Django busca plantillas allí.

Esto significa que puedes almacenar plantillas con tus aplicaciones individuales. Esto también ayuda a distribuir aplicaciones de Django con plantillas predeterminadas.

Por ejemplo, para este ajuste:

INSTALLED_APPS = ["myproject.polls", "myproject.music"]

…entonces get_template('foo.html') buscará foo.html en estos directorios, en este orden:

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

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

Y utilizará el primero que encuentre.

El orden de INSTALLED_APPS es significativo! Por ejemplo, si deseas personalizar la interfaz administrativa de Django, podrías elegir sobrescribir el estándar admin/base_site.html del template, desde django.contrib.admin, con tu propio admin/base_site.html en myproject.polls. Debes asegurarte entonces de que tu myproject.polls venga antes de django.contrib.admin en INSTALLED_APPS, de lo contrario el de django.contrib.admin se cargará primero y el tuyo será ignorado.

Ten en cuenta que el cargador realiza una optimización cuando se ejecuta por primera vez: almacena una lista de los paquetes de INSTALLED_APPS que tienen un subdirectorio templates.

Puedes habilitar este cargador estableciendo APP_DIRS en True:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "APP_DIRS": True,
    }
]

django.template.loaders.cached.Loader

class cached.Loader

Si bien el sistema de plantillas de Django es bastante rápido, si necesita leer y compilar tus plantillas cada vez que se renderizan, la sobrecarga desde ese punto puede acumularse.

Configuras el cargador de plantillas cacheado con una lista de otros cargadores que debería envolver. Los cargadores envueltos se utilizan para localizar plantillas desconocidas cuando se encuentran por primera vez. El cargador cacheado almacena luego la compilación Template en memoria. La instancia cacheada Template se devuelve para solicitudes posteriores a cargar la misma plantilla.

Este cargador está habilitado automáticamente si no se especifica OPTIONS['loaders'].

Puedes especificar manualmente el almacenamiento en caché de plantillas con algunos cargadores de plantillas personalizados utilizando configuraciones como esta:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.cached.Loader",
                    [
                        "django.template.loaders.filesystem.Loader",
                        "django.template.loaders.app_directories.Loader",
                        "path.to.custom.Loader",
                    ],
                ),
            ],
        },
    }
]

Nota

Todos los tags de plantilla integrados de Django son seguros para usar con el cargador cacheado, pero si estás usando tags de plantilla personalizados que provienen de paquetes de terceros o que escribiste tú mismo, asegúrate de que la implementación Node de cada tag sea thread-safe. Para obtener más información, consulta consideraciones sobre seguridad en hilos para los tags de plantilla.

django.template.loaders.locmem.Loader

class locmem.Loader

Carga plantillas desde un diccionario de Python. Esto es útil para pruebas.

Este cargador toma como primer argumento un diccionario de plantillas:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.locmem.Loader",
                    {
                        "index.html": "content here",
                    },
                ),
            ],
        },
    }
]

Este cargador está deshabilitado por defecto.

Django utiliza los cargadores de plantillas en orden según la opción 'loaders'. Utiliza cada cargador hasta que un cargador encuentra una coincidencia.

Cargadores personalizados

Es posible cargar plantillas desde fuentes adicionales utilizando cargadores de plantillas personalizados. Los clases Loader personalizadas deben heredar de django.template.loaders.base.Loader y definir los métodos get_contents() y get_template_sources().

Métodos del cargador

class Loader[fuente]

Carga plantillas desde una fuente dada, como el sistema de archivos o una base de datos.

get_template_sources(template_name)[fuente]

Un método que toma un template_name y devuelve instancias de Origin para cada posible fuente.

Los textos traducidos son:

El método no necesita verificar si la plantilla existe en un camino dado, pero debe asegurarse de que el camino es válido. Por ejemplo, el cargador filesystem se asegura de que el camino esté bajo un directorio de plantillas válido.

get_contents(origin)

Devuelve los contenidos para una plantilla dada una instancia de Origin.

Esto es donde un cargador filesystem leería contenido del sistema de archivos, o un cargador de base de datos leería desde la base de datos. Si no existe una plantilla que coincida, esto debería levantar un error TemplateDoesNotExist.

get_template(template_name, skip=None)[fuente]

Devuelve un objeto Template para una plantilla dada un template_name mediante iteración sobre los resultados de get_template_sources() y llamando a get_contents(). Esto devuelve la primera plantilla que coincida. Si no se encuentra ninguna plantilla, se levanta TemplateDoesNotExist.

El argumento opcional skip es una lista de orígenes a ignorar al extender plantillas. Esto permite a las plantillas extender otras plantillas con el mismo nombre. También se utiliza para evitar errores de recursividad.

En general, basta con definir get_template_sources() y get_contents() para cargadores de plantillas personalizados. get_template() suele no necesitar ser sobrescrito.

Construyendo tu propio

Para ejemplos, lee el código fuente de los cargadores incorporados de Django <django/template/loaders>.

Origen de plantilla

Los templates tienen un origin que contiene atributos dependientes de la fuente desde la que se cargan.

class Origin(name, template_name=None, loader=None)[fuente]
name

La ruta al template tal como lo devuelve el cargador de plantillas. Para los cargadores que leen desde el sistema de archivos, esta es la ruta completa al template.

Si el template se instancia directamente en lugar de a través de un cargador de plantillas, este es una cadena con valor <unknown_source>.

template_name

La ruta relativa al template tal como se pasa al cargador de plantillas.

Si el template se instancia directamente en lugar de a través de un cargador de plantillas, este es None.

loader

La instancia del cargador de plantillas que construyó este Origin.

Si el template se instancia directamente en lugar de a través de un cargador de plantillas, este es None.

El cargador django.template.loaders.cached.Loader requiere que todos sus cargadores envueltos establezcan esta atributo, típicamente instanciando el Origin con loader=self.