================================ Capitulo 9: Plantillas Avanzadas ================================
Aunque la mayor parte de tu interacción con el sistema de plantillas (templates) de Django será en el rol de autor, probablemente quieras algunas veces modificar y extender el sistema de plantillas -- así sea para agregar funcionalidad, o para hacer tu trabajo más fácil de alguna otra manera.
Este capítulo se adentra en el sistema de plantillas de Django, cubriendo todo lo que necesitas saber, ya sea por si planeas extender el sistema, o por si sólo eres curioso acerca de su funcionamiento. Además cubre la característica de auto-escape, medida de seguridad que seguramente notaras con el paso del tiempo si continuas usando Django.
Si estás tratando de utilizar el sistema de plantillas de Django como parte de otra aplicación, es decir, sin el resto del framework, asegúrate de leer la sección "Configurando el Sistema de plantillas en modo autónomo" mas adelante en este mismo capítulo.
Primero, vamos a recordar algunos términos presentados en el :doc:`capítulo 4<chapter04>`:
Una plantilla es un documento de texto, o un string normal de Python marcado con la sintaxis especial del lenguaje de plantillas de Django. Una plantilla puede contener etiquetas de bloque (block tags) y variables.
Una etiqueta de bloque es un símbolo dentro de una plantilla que hace algo. Esta definición es así de vaga a propósito. Por ejemplo, una etiqueta de bloque puede producir contenido, servir como estructura de control (una sentencia
if
o un loopfor
), obtener contenido de la base de datos, o habilitar acceso a otras etiquetas de plantilla.Las etiquetas de bloque deben ser rodeadas por
{%
y%}
{% if is_logged_in %} ¡Gracias por identificarte! {% else %} Por favor identificate. {% endif %}
Una variable es un símbolo dentro de una plantilla que emite un valor.
Las etiquetas de variable deben ser rodeadas por
{{
y}}
:Mi nombre es {{ nombre }}. Mis apellidos son {{ apellidos }}.
Un contexto es un mapeo entre nombres y valores (similar a un diccionario de Python) que es pasado a una plantilla.
Una plantilla renderiza un contexto reemplazando los "huecos" que dejan las variables por valores tomados del contexto y ejecutando todas las etiquetes de bloque.
El resto de este capítulo discute las distintas maneras de extender el sistema de plantillas. Aunque primero, debemos dar una mirada a algunos conceptos internos que quedaron fuera del :doc:`capítulo 4<chapter04>` por simplicidad.
Cuando una plantilla debe ser renderizada, necesita un contexto. Usualmente este
contexto es una instancia de django.template.Context
, pero Django también
provee una subclase especial: django.template.RequestContext
que actúa de
una manera levemente diferente. RequestContext
agrega muchas variables al
contexto de nuestra plantilla -- cosas como el objeto HttpRequest
o
información acerca del usuario que está siendo usado actualmente. El atajo
render()
crea un RequestContext
a menos que explícitamente se le pase
una instancia de contexto diferente.
Usa RequestContext
cuando no quieras especificar el mismo conjunto de
variables una y otra vez en una serie de plantillas. Por ejemplo, considera
estas dos vistas:
from django.template import loader, Context
def vista_1(request):
# ...
t = loader.get_template('plantilla.html')
c = Context({
'aplicacion: 'Biblioteca',
'usuario': request.user,
'direccion_ip': request.META['REMOTE_ADDR'],
'mensaje': 'Soy una vista.'
})
return t.render(c)
def vista_2(request):
# ...
t = loader.get_template('plantilla2.html')
c = Context({
'aplicacion: 'Biblioteca',
'usuario': request.user,
'direccion_ip': request.META['REMOTE_ADDR'],
'mensaje': 'Soy otra vista.'
})
return t.render(c)
A propósito no hemos usado el atajo render()
en estos ejemplos
-- manualmente cargamos las plantillas, construimos el contexto y
renderizamos las plantillas. Simplemente por claridad, estamos demostrando todos
los pasos necesarios.
Cada vista pasa las mismas tres variables -- aplicacion
, usuario
y directamente_ip
a la plantilla. ¿No sería bueno poder eliminar esa redundancia?
Las peticiones de contexto (RequestContext
) y los procesadores de
contexto (Context Processors
) fueron creado para resolver este problema.
Los procesadores de contexto te permiten especificar un número de variables que
son incluidas automáticamente en cada contexto -- sin la necesidad de tener que
hacerlo manualmente en cada llamada a render()
. El secreto está en
utilizar RequestContext
en lugar de Context
cuando renderices una
plantilla.
La forma de nivel más bajo de usar procesadores de contexto es crear algunos de
ellos y pasarlos a RequestContext
. A continuación mostramos como el ejemplo
anterior puede lograrse utilizando procesadores de contexto:
from django.template import loader, RequestContext
def custom_proc(request):
"Un procesador de contexto que provee 'aplicacion', 'usuario' y'directamente_ip'."
return {
'aplicacion: 'Biblioteca',
'usuario': request.user,
'direccion_ip': request.META['REMOTE_ADDR'],
}
def vista_1(request):
# ...
t = loader.get_template('plantilla1.html')
c = RequestContext(request, {'mensaje': 'Soy la vista 1.'},
processors=[custom_proc])
return t.render(c)
def vista_2(request):
# ...
t = loader.get_template('plantilla2.html')
c = RequestContext(request, {'mensaje': 'Soy la vista 2.'},
processors=[custom_proc])
return t.render(c)
Inspeccionemos paso a paso este código:
- Primero, definimos una función
custom_proc
. Este es un procesador de contexto -- toma un objetoHttpRequest
y devuelve un diccionario con variables a usar en el contexto de la plantilla. Eso es todo lo que hace. - Hemos cambiado las dos vistas para que usen
RequestContext
en lugar deContext
. Hay dos diferencias en cuanto a cómo el contexto es construido. Uno,RequestContext
requiere que el primer argumento sea una instancia deHttpRequest
-- la cual fue pasada a la vista en primer lugar (request
). Dos,RequestContext
recibe un parámetro opcionalprocessors
, el cual es una lista o una tupla de funciones procesadoras de contexto a utilizar. En este caso, pasamoscustom_proc
, a nuestro procesador de contexto definido previamente. - Ya no es necesario en cada vista incluir
aplicacion
,usuario
ydirección_ip
cuando construimos el contexto, ya que ahora estas variables son provistas porcustom_proc
. - Cada vista aún posee la flexibilidad como para introducir una o más
variables en el contexto de la plantilla si es necesario. En este
ejemplo, la variable de plantilla
mensaje
es creada de manera diferente en cada una de las vistas.
En él :doc:`capítulo 4<chapter04>`, presentamos el atajo render()
, el cual
nos ahorra tener que llamar a loader.get_template()
, luego crear un
Context
y además, llamar al método render()
en la plantilla. Para
demostrar el funcionamiento a bajo nivel de los procesadores de contexto, en
los ejemplos anteriores no hemos utilizado render()
, pero es posible -- y
preferible utilizar los procesadores de contexto junto a render()
. Esto lo
logramos mediante el argumento context_instance
de la siguiente manera:
from django.shortcuts import render
from django.template import RequestContext
def custom_proc(request):
" Un procesador de contexto que provee 'aplicacion', 'usuario' y 'directamente_ip'."
return {
'aplicacion: 'Biblioteca',
'usuario': request.user,
'direccion_ip': request.META['REMOTE_ADDR'],
}
def vista2(request):
# ...
return render(request, 'plantilla1.html',
{'mensaje': 'Soy la vista 1.'},
context_instance=RequestContext(request, processors=[custom_proc]))
def vista2(request):
# ...
return render(request, 'template2.html',
{'mensaje': 'Soy la vista 2.'},
context_instance=RequestContext(request, processors=[custom_proc]))
Aquí, hemos logrado reducir el código para renderizar las plantillas en cada vista a una sola línea.
Esto es una mejora, pero, evaluando la concisión de este código, debemos
admitir que hemos logrado reducir la redundancia en los datos
(nuestras variables de plantilla), pero aun así, estamos especificando una
y otra vez nuestro contexto. Es decir, hasta ahora usar procesadores de
contexto no nos ahorra mucho código si tenemos que escribir processors
constantemente.
Por esta razón, Django admite el uso de procesadores de contexto globales. El
parámetro de configuración TEMPLATE_CONTEXT_PROCESSORS
designa cuales serán
los procesadores de contexto que deberán ser aplicados siempre a
RequestContext
. Esto elimina la necesidad de especificar processors
cada
vez que utilizamos RequestContext
.
TEMPLATE_CONTEXT_PROCESSORS
tiene, por omisión, el siguiente valor:
TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.media', )
Este parámetro de configuración es una tupla de funciones que utilizan la
misma interfaz que nuestra función custom_proc
utilizada previamente --
funciones que toman un objeto HttpRequest
como primer argumento, y
devuelven un diccionario de items que serán incluidos en el contexto de la
plantilla. Ten en cuenta que los valores en TEMPLATE_CONTEXT_PROCESSORS
son especificados como cadenas, lo cual significa que estos procesadores
deberán estar en algún lugar dentro de tu PYTHONPATH
(para poder referirse
a ellos desde el archivo de configuración)
Estos procesadores de contexto son aplicados en orden, es decir, si uno de estos procesadores añade una variable al contexto y un segundo procesador añade otra variable con el mismo nombre, entonces la segunda sobre-escribirá a la primera.
Django provee un numero de procesadores de contexto simples, entre ellos los que están activos por defecto.
Si TEMPLATE_CONTEXT_PROCESSORS
contiene este procesador, cada
RequestContext
contendrá las siguientes variables:
user
: Una instancia dedjango.contrib.auth.models.User
representando al usuario actualmente autenticado (o una instancia deAnonymousUser
si el cliente no se ha autenticado aún).messages
: Una lista de mensajes (como string) para el usuario actualmente autenticado. Detrás del telón, esta variable llama arequest.user.get_and_delete_messages()
para cada request. Este método colecta los mensajes del usuario, y luego los borra de la base de datos.perms
: Instancia dedjango.core.context_processors.PermWrapper
, la cual representa los permisos que posee el usuario actualmente autenticado.
En él :doc:`capítulo 14<chapter14>` encontrarás más información acerca de usuarios, permisos y mensajes.
Este procesador agrega información de depuración a la capa de plantillas.
Si TEMPLATE_CONTEXT_PROCESSORS
contiene este procesador, cada
RequestContext
contendrá las siguientes variables:
debug
: El valor del parámetro de configuraciónDEBUG
(True
oFalse
). Esta variable puede usarse en las plantillas para saber si estás en modo de depuración o no.sql_queries
: Una lista de diccionarios{'sql': ..., 'time': ...}
representando todas las consultas SQL que se generaron durante la petición (request) y cuánto duraron. La lista está ordenada respecto a cuándo fue ejecutada cada consulta.
Como la información de depuración es sensible, este procesador de contexto sólo agregará las variables al contexto si las dos siguientes condiciones son verdaderas.
- El parámetro de configuración
DEBUG
es True - La solicitud (request) viene de una dirección IP listada en el parámetro
de configuración
INTERNAL_IPS
.
Los lectores astutos se darán cuenta, que la variable de plantilla debug
nunca tiene el valor False
porque si, DEBUG
es False
, las demás
variables de plantillas que dependen de debug
, no podrán cargarse en
primer lugar.
Si este procesador está habilitado, cada RequestContext
contendrá las
siguientes variables:
LANGUAGES
: El valor del parámetro de configuraciónLANGUAGES
.LANGUAGE_CODE
:request.LANGUAGE_CODE
si existe; de lo contrario, el valor del parámetro de configuraciónLANGUAGE_CODE
.
En el Apéndice D se especifica más información sobre estos parámetros.
Si este procesador está habilitado, cada RequestContext
contendrá una
variable request
, la cual es el actual objeto HttpRequest
. Observa que
este procesador no está habilitado por defecto; tú tienes que activarlo.
Tal vez quieras usarlo, si necesitas que tus plantillas tengan acceso
a los atributos de la actual HttpRequest
tal como la dirección IP:
{{ request.REMOTE_ADDR }}
Algunos puntos a tener en cuenta:
- Cada procesador de contexto debe ser responsable por la mínima cantidad de funcionalidad posible. Usar muchos procesadores es algo sencillo, es por eso que dividir la funcionalidad de tu procesador de manera lógica puede ser útil para poder reutilizarlos en el futuro.
- Ten presente que cualquier procesador de contexto en
TEMPLATE_CONTEXT_PROCESSORS
estará disponible en cada plantilla cuya configuración esté dictada por ese archivo de configuración, así que trata de seleccionar nombres de variables con pocas probabilidades de entrar en conflicto con nombre de variables que tus plantillas pudieran usar en forma independiente. Como los nombres de variables son sensibles a mayúsculas/minúsculas no es una mala idea usar mayúsculas para las variables provistas por un procesador. - No importa dónde residan en el sistema de archivos, mientras se hallen en
tu ruta de Python de manera que puedas incluirlos en tu variable de
configuración
TEMPLATE_CONTEXT_PROCESSORS
. Habiendo dicho eso, diremos también que la convención es grabarlos en un archivo llamadocontext_processors.py
ubicado en tu aplicación o en tu proyecto.
Cuando generamos HTML por medio de plantillas, siempre existe el riesgo de incluir variables que contengan caracteres que afecten la salida del HTML. Por ejemplo, considera el siguiente fragmento de una plantilla:
Hola, {{ nombre }}.
Esto parece inofensivo al principio, ya que solo muestra el nombre de un usuario, pero considera lo siguiente: que pasaria si el usuario introduce su nombre de la siguiente manera:
<script>alert('hola')</script>
Con este valor, la plantilla renderizaria el nombre así:
Hola, <script>alert('hola')</script>
Lo cual daría como resultado que el navegador mostrara una caja de alerta. De
igual forma, si el nombre contiene símbolos como este '<'
:
<b>usernombre
Esto daría como resultado una plantilla renderizada así:
Hola, <b>usernombre
Lo cual a su vez daría como resultado que el nombre apareciera en negritas.
Claramente, no deberías confiar en lo que los usuarios suben ciegamente, ni tampoco deberías permitir que inserten datos directamente en las páginas web, porque algún usuario malicioso podría aprovecharse de huecos como estos, para hacer cosas potencialmente dañinas. Este tipo de de riesgos de seguridad es llamado ataque Cross Site Scripting (XSS) (Scripting inter-sitio). Consulta el :doc:`capítulo 20<chapter20>` , para conocer más sobre seguridad.
Para evitar este problema, tienes dos opciones:
- Uno, asegúrate de que cada una de las variables "no confiables", sean pasadas
a través de un filtro
escape
, el cual convierte caracteres potencialmente dañinos en no dañinos. Esta fue la solución por defecto en Django durante los primeros años, pero el problema es que pone toda la responsabilidad en ti que como desarrollador/autor de plantillas, debes asegurarte de escapar todo. Es fácil olvidar escapar datos. - Dos, puedes tomar ventaja de que Django automáticamente escapa el HTML. El resto de esta sección describe como trabaja el auto-escape.
Por defecto en Django, cada plantilla se encarga automáticamente de escapar la salida de cada etiqueta de variable. Es particular estos cinco caracteres son escapados:
<
es convertido a <
>
es convertido a >
'
(comillas simples) son convertidas a '
"
(comillas dobles) son convertidas a "
&
es convertido a &
De nuevo, hacemos énfasis en que este comportamiento se da por defecto. Si estas usando el sistema de plantillas, estas protegido.
Si no quieres que los datos sean escapados automáticamente en el sitio, en algún nivel de las plantillas o a nivel de variables, puedes desactivar este comportamiento de diferentes formas.
¿Porque querrías desactivarlo? Porque algunas veces, las variables de plantillas contienen datos que intentas renderizar como HTML en crudo, en cuyo caso necesitas que el contenido no sea escapado. Por ejemplo, si quisieras almacenar algunos datos confiables en HTML en la base de datos y quisieras incrustarlos directamente en la plantilla o si quieres usar el sistema de plantillas para producir texto que no sea HTML --Tal como un mensaje de email, para una instancia.
Para desactivar el auto-escape para una variable individual, usa el filtro
safe
:
Esto sera escapado: {{ datos }} Esto no sera escapado {{ datos|safe }}
Piensa en safe como el nombre corto para safe from further escaping
o puede ser interpretado de forma segura como HTML En este ejemplo, si
datos
contiene '<b>'
, la salida será:
Esto sera escapado: <b> Esto no sera escapado: <b>
Para controlar el auto-escapado de una plantilla, envuelve la plantilla (o solo
una sección en particular) con la etiqueta autoescape
, de esta forma:
{% autoescape off %} Hola {{ nombre }} {% endautoescape %}
La etiqueta autoescape
toma cualquiera de estos argumentos: on
u off
.
Ocasionalmente, tal vez quieras forzar el auto-escape, cuando este desactivado.
Esta es una plantilla de ejemplo:
Auto-escape activado por defecto. Hola {{ nombre }} {% autoescape off %} Esto no sera auto-escapado: {{ datos }}. Esto tampoco: {{ otros_datos }} {% autoescape on %} Auto-escape aplicado otra vez: {{ nombre }} {% endautoescape %} {% endautoescape %}
La etiqueta de auto-escape pasa sus efectos a las plantillas que extiende, así
como a las plantillas incluidas vía la etiqueta include
, tal como lo hacen
todos los bloques de etiquetas. Por ejemplo:
# base.html {% autoescape off %} <h1>{% block title %}{% endblock %}</h1> {% block content %} {% endblock %} {% endautoescape %} # child.html {% extends "base.html" %} {% block title %}Bienvenidos{% endblock %} {% block content %}{{ saludo }}{% endblock %}
Una vez que el auto-escape es desactivado en la plantilla base, también puede
ser desactivado en las plantillas hijas, resultando en el siguiente HTML
renderizado cuando la variable saludo
contiene la cadena <b>Hola!</b>
:
<h1>Bienvenidos</h1> <b>Hola!</b>
Nota
En general, los autores de plantillas no necesitan preocuparse por auto-escapar mucho. Los desarrolladores del lado de Python (personas que escriben vista y filtros a la medida) necesitan pensar en los casos en los cuales sus datos no sean escapados y marcarlos apropiadamente, pensando en la forma en que trabajan las plantillas.
Si estas creando plantillas, para situaciones en las que no estás seguro si
el auto-escape esta activado, puedes agregar un filtro escape
a cualquier
variable que necesite ser escapada. Cuando el auto-escape esta activado, no
hay peligro de el filtro escape
o del doble-escapado de datos -- el
filtro escape
no afecta las variables que ya han ha sido escapadas.
Como mencionamos anteriormente, los argumentos de algunos filtros pueden ser cadenas:
{{ datos|default:"Esta es una cadena literal." }}
Todas las cadenas literales son insertadas sin escape automático en la plantilla
-- actuan como si fueran pasadas por el filtro safe
. La razón detrás
de todo esto, es que los autores de plantillas tienen el control de lo que
pasan dentro de las cadenas literales, así que se aseguran que el texto
sea escapado correctamente cuando la plantilla es escrita.
Por lo que escribirías
{{ datos default:"3 < 2" }}
En lugar de
{{ datos default:"3 < 2" }} <-- Mal! No hagas esto.
Esto no afecta el comportamiento de los datos, que provienen de la misma variable. El contenido de las variable todavía es escapado automáticamente si es necesario, porque va mas allá de el control del autor de la plantilla.
En general las plantillas se almacenan en archivos en el sistema de archivos, pero puedes usar cargadores de plantillas personalizados (template loaders) para cargar plantillas desde otros orígenes.
Django tiene dos maneras de cargar plantillas:
django.template.loader.get_template(template)
:get_template
retorna la plantilla compilada (un objetoTemplate
) para la plantilla con el nombre provisto. Si la plantilla no existe, se generará una excepciónTemplateDoesNotExist
.django.template.loader.select_template(template_nombre_list)
:select_template
es similar aget-template
, excepto que recibe una lista de nombres de plantillas. Retorna la primera plantilla de dicha lista que existe. Si ninguna de las plantillas existe se lanzará una excepciónTemplateDoesNotExist
.
Como se vio en él :doc:`capítulo 4<chapter04>`, cada una de estas funciones usan
por omisión el valor de la variable de configuración TEMPLATE_DIRS
para
cargar las plantillas. Sin embargo, internamente las mismas delegan la tarea
pesada a un cargador de plantillas.
Algunos de los cargadores están, por omisión, desactivados pero puedes
activarlos editando la variable de configuración TEMPLATE_LOADERS
.
TEMPLATE_LOADERS
debe ser una tupla de cadenas, donde cada cadena representa
un cargador de plantillas. Estos son los cargadores de plantillas incluidos con
Django:
django.template.loaders.filesystem.load_template_source
: Este cargador carga plantillas desde el sistema de archivos, de acuerdo aTEMPLATE_DIRS
. Por omisión está activo.django.template.loaders.app_directories.load_template_source
: Este cargador carga plantillas desde aplicaciones Django en el sistema de archivos. Para cada aplicación enINSTALLED_APPS
, el cargador busca un sub-directoriotemplates
. Si el directorio existe, Django buscará una plantilla en el mismo.Esto significa que puedes almacenar plantillas en tus aplicaciones individuales, facilitando la distribución de aplicaciones Django con plantillas por omisión. Por ejemplo si
INSTALLED_APPS
contiene('misitio.blog', 'missitio.musica')
entoncesget_template('foo.html')
buscará plantillas en el siguiente orden:/ruta/a/misitio/blog/templates/foo.html
/ruta/a/misitio/musica/templates/foo.html
Notar que el cargador realiza una optimización cuando es importado por primera vez: hace caching de una lista de cuáles de los paquetes en
INSTALLED_APPS
tienen un sub-directoriotemplates
.Por omisión este cargador está activado.
django.template.loaders.eggs.load_template_source
: Este cargador es básicamente idéntico aapp_directories
, excepto que carga las plantillas desde eggs Python en lugar de hacerlo desde el sistema de archivos. Por omisión este cargador está desactivado; necesitarás activarlo si estás usando eggs para distribuir tu aplicación.
Django usa los cargadores de plantillas en el orden en el que aparecen en la
variable de configuración TEMPLATE_DIRS
. Usará cada uno de los cargadores
hasta que uno de los mismos tenga éxito en la búsqueda de la plantilla.
Ahora que entiendes un poco más acerca del funcionamiento interno del sistema de plantillas, echemos una mirada a cómo extender el sistema con código propio.
La mayor parte de la personalización de plantillas se da en forma de etiquetas y/o filtros. Aunque el lenguaje de plantillas de Django incluye muchos, probablemente diseñaras tus propias bibliotecas de etiquetas y filtros que se adapten a tus propias necesidades. Afortunadamente, es muy fácil definir tu propia funcionalidad.
Ya sea que estés escribiendo etiquetas o filtros personalizados, la primera tarea a realizar es crear una biblioteca para etiquetas -- un pequeño fragmento de infraestructura con el cual Django puede interactuar.
La creación de una biblioteca para etiquetas es un proceso de dos pasos:
Primero, decidir qué aplicación Django alojará el directorio. Si has creado una aplicación vía
manage.py startapp
puedes colocarla allí, o puedes crear otra aplicación con el solo fin de alojar la biblioteca.Sin importar cuál de las dos rutas tomes, asegúrate de agregar la aplicación a tu variable de configuración
INSTALLED_APPS
. Explicaremos esto un poco más adelante.Segundo, crear un directorio
templatestags
en el paquete de aplicación Django apropiado. Debe encontrarse en el mismo nivel quemodels.py
,views.py
, etc. Por ejemplo mira el siguiente directorio:biblioteca/ __init__.py admin.py forms.py models.py templates/ indice.html templatestags/ __init__.py etiquetas.py views.py
Crea dos archivos vacíos en el directorio
templatetags
: un archivo__init__.py
(para indicarle a Python que se trata de un paquete que contiene código Python) y un archivo que contendrá tus definiciones personalizadas de etiquetas/filtros. El nombre del segundo archivo es el que usarás para cargar las etiquetas más tarde. Por ejemplo, si tus etiquetas/filtros personalizadas están en un archivo llamadoetiquetas.py
, entonces deberás escribir lo siguiente en una plantilla:{% load etiquetas %}
La etiqueta
{% load %}
examina tu variable de configuraciónINSTALLED_APPS
y sólo permite la carga de bibliotecas para plantillas desde aplicaciones Django que estén instaladas. Se trata de una característica de seguridad; te permite tener en cierto equipo el código Python de varias bibliotecas para plantillas sin tener que activar el acceso a todas ellas para cada instalación de Django.
Si escribes una biblioteca para etiquetas que no se encuentra atada a ningún
modelo/vista particular es válido y normal el tener un paquete de aplicación
Django que sólo contiene un paquete templatetags
. No existen límites en lo
referente a cuántos módulos puedes poner en el paquete templatetags
. Sólo
ten presente que una sentencia {% load %}
cargará etiquetas/filtros para el
nombre del módulo Python provisto, no el nombre de la aplicación.
Una vez que has creado ese módulo Python, sólo tendrás que escribir un poquito de código Python, dependiendo de si estás escribiendo filtros o etiquetas.
Para ser una biblioteca de etiquetas válida, el módulo debe contener una
variable a nivel del módulo llamada register
que sea una instancia de
template.Library
. Esta instancia de template.Library
es la estructura de
datos en la cual son registradas todas las etiquetas y filtros. Así que inserta
en la zona superior de tu módulo, lo siguiente:
from django import template register = template.Library()
Nota:
Para ver un buen número de ejemplos, examina el código fuente de los filtros
y etiquetas incluidos con Django. Puedes encontrarlos en
django/template/defaultfilters.py y django/template/defaulttags.py,
respectivamente. Algunas aplicaciones en django.contrib
también
contienen bibliotecas para plantillas.
Una vez que hayas creado esta variable register
, usarás la misma para crear
filtros y etiquetas para plantillas.
Los filtros personalizados son sólo funciones Python que reciben uno o dos argumentos:
- El valor de la variable (entrada)
- El valor del argumento, el cual puede tener un valor por omisión o puede ser obviado.
Por ejemplo, en el filtro {{ var|foo:"bar" }}
el filtro foo
recibiría el
contenido de la variable var
y el argumento "bar"
.
Las funciones filtro deben siempre retornar algo. No deben arrojar excepciones, y deben fallar silenciosamente. Si existe un error, las mismas deben retornar la entrada original o una cadena vacía, dependiendo de qué sea más apropiado.
Este es un ejemplo de la definición de un filtro:
def cortar(value, arg): "Remueva todos los valores que concuerdan con los argumentos de la cadena dada" return value.replace(arg, '')
Y este es un ejemplo de cómo se usaría:
{{ unavariable|cortar:"0" }}
La mayoría de los filtros no reciben argumentos. En ese caso, basta con que no incluyas el argumento en tu función:
def minusculas(value): # Unicamente un argumento. "Convierte una cadena a minusculas" return value.lower()
Una vez que has escrito tu definición de filtro, necesitas registrarlo en una
instancia de Library
, para que esté disponible para el lenguaje de
plantillas de Django:
register.filter('cortar', cortar) register.filter('minusculas', minusculas)
El método Library.filter()
tiene dos argumentos:
- El nombre del filtro (una cadena)
- La función filtro propiamente dicha
Si estás usando Python 2.7 o una version superior, puedes usar register.filter()
como un decorador:
@register.filter(name='cut') def cortar(value, arg): return value.replace(arg, '') @register.filter def minusculas(value): return value.lower()
Si no provees el argumento name
, como en el segundo ejemplo, Django usará el
nombre de la función como nombre del filtro.
Veamos entonces el ejemplo completo de una biblioteca para plantillas, que
provee el filtro cortar
:
from django import template register = template.Library() @register.filter(name='cortar') def cortar(value, arg): return value.replace(arg, '')
Las etiquetas son más complejas que los filtros porque las etiquetas pueden implementar prácticamente cualquier funcionalidad.
El :doc:`capítulo 4<chapter04>` describe cómo el sistema de plantillas funciona en un proceso de dos etapas: compilación y renderizado. Para definir una etiqueta de plantilla personalizada, necesitas indicarle a Django cómo manejar ambas etapas cuando llega a tu etiqueta.
Cuando Django compila una plantilla, divide el texto crudo de la plantilla en
nodos. Cada nodo es una instancia de django.template.Node
y tiene un
método render()
. Por lo tanto, una plantilla compilada es simplemente una
lista de objetos Node
. Por ejemplo considera esta plantilla:
Hola, {{ persona.nombre }}. {% ifequal nombre.birthday today %} Happy birthday! {% else %} Be sure to come back on your birthday for a splendid surprise message. {% endifequal %}
En un formulario compilado, esta plantilla es representada como una lista de nodos:
- Nodo texto:
"Hola, "
- Nodo variable:
person.nombre
- Nodo texto:
".\n\n"
- Nodo IfEqual :
nombre.birthday
ytoday
Cuando llamas a render()
en una plantilla compilada, la plantilla llama a
render()
en cada Node()
de su lista de nodos, con el contexto
proporcionado. Los resultados son todos concatenados juntos para formar la
salida de la plantilla. A si que para definir una etiqueta de plantilla
personalizada debes especificar cómo se debe convertir la etiqueta en crudo en
un Node
(la función de compilación) y definir lo qué hace el método render()
del nodo.
En las siguientes secciones explicaremos los pasos necesarios para escribir una etiqueta propia.
Para cada etiqueta de plantilla que encuentra, el intérprete (parser) de
plantillas llama a una función de Python pasándole el contenido de la etiqueta y
el objeto parser en sí mismo. Esta función tiene la responsabilidad de retornar
una instancia de Node
basada en el contenido de la etiqueta.
Por ejemplo, escribamos una etiqueta {% current_time %}
que visualice la
fecha/hora actuales con un formato determinado por un parámetro pasado a la
etiqueta, usando la sintaxis de strftime
(consulta
http://www.djangoproject.com/r/python/strftime/
, para mas detalles). Es una
buena idea definir la sintaxis de la etiqueta previamente. En nuestro caso,
supongamos que la etiqueta deberá ser usada de la siguiente manera:
<p>La fecha actual es {% fecha_actual "%Y-%m-%d %I:%M %p" %}.</p>
Nota:
Si, esta etiqueta de plantilla es redundante -- La etiqueta {% now %}
incluida en Django por defecto hace exactamente lo mismo con una sintaxis
más simple. Sólo mostramos esta etiqueta a modo de ejemplo.
Para evaluar esta función, se deberá obtener el parámetro y crear el objeto
Node
:
from django import template register = template.Library() def fecha_actual(parser, token): try: # El metodo split_contents() sabe como dividir cadenas entre comillas. tag_nombre, formato_cadena = token.split_contents() except ValueError: msg = '%r la etiqueta requiere un simple argumento' % token.split_contents()[0] raise template.TemplateSyntaxError(msg) return NodoFechaActual(formato_cadena[1:-1])
Hay muchas cosas en juego aquí:
parser
es la instancia del parser. No lo necesitamos en este ejemplo.token.contents
es un cadena con los contenidos crudos de la etiqueta, en nuestro ejemplo sería: 'fecha_actual "%Y-%m-%d %I:%M %p"'.- El método
token.split_contents()
separa los argumentos en sus espacios, mientras deja unidas a las cadenas. Evite utilizartoken.contents.split()
(el cual usa la semántica natural de Python para dividir cadenas, y por esto no es tan robusto, ya que divide en todos los espacios, incluyendo aquellos dentro de cadenas entre comillas. - Esta función es la responsable de generar la excepción
django.template.TemplateSyntaxError
con mensajes útiles, ante cualquier caso de error de sintaxis. - No escribas el nombre de la etiqueta en el mensaje de error, ya que eso
acoplaría innecesariamente el nombre de la etiqueta a la función. En
cambio,
token.split_contents()[0]
siempre contendrá el nombre de tu etiqueta -- aún cuando la etiqueta no lleve argumentos. - La función devuelve
NodoFechaActual
(el cual mostraremos en un momento) conteniendo todo lo que el nodo necesita saber sobre esta etiqueta. En este caso, sólo pasa el argumento"%Y-%m-%d %I:%M %p"
. Las comillas son removidas conformat_string[1:-1]
. - Las funciones de compilación de etiquetas de plantilla deben devolver
una subclase de
Nodo
; cualquier otro valor es un error.
El segundo paso para escribir etiquetas propias, es definir una subclase de
Node
que posea un método render()
. Continuando con el ejemplo previo,
debemos definir CurrentTimeNode
:
import datetime class NodoFechaActual(template.Node): def __init__(self, formato_cadena): self.formato_cadena = str(formato_cadena) def render(self, context): ahora = datetime.datetime.now() return ahora.strftime(self.formato_cadena)
Estas dos funciones (__init__
y render
) se relacionan directamente
con los dos pasos para el proceso de la plantilla (compilación y renderizado).
La función de inicialización sólo necesitará almacenar el string con el formato
deseado, el trabajo real sucede dentro de la función render()
Del mismo modo que los filtros de plantilla, estas funciones de renderización deberían fallar silenciosamente en lugar de generar errores. En el único momento en el cual se le es permitido a las etiquetas de plantilla generar errores es en tiempo de compilación.
Finalmente, deberás registrar la etiqueta con tu objeto Library
dentro del
módulo. Registrar nuevas etiquetas es muy similar a registrar nuevos filtros
(como explicamos previamente). Sólo deberás instanciar un objeto
template.Library
y llamar a su método tag()
. Por ejemplo:
register.tag('fecha_actual', fecha_actual)
El método tag()
toma dos argumentos:
- El nombre de la etiqueta de plantilla (string). Si esto se omite, se utilizará el nombre de la función de compilación.
- La función de compilación.
De manera similar a como sucede con el registro de filtros, también es posible utilizar
register.tag
como un decorador en Python 2.7 o posterior:
@register.tag(name="fecha_actual") def fecha_actual(parser, token): # ... @register.tag def fecha_actual(parser, token): # ...
Si omitimos el argumento name
, así como en el segundo ejemplo, Django
usará el nombre de la función como nombre de la etiqueta.
El ejemplo en la sección anterior simplemente devuelve un valor. Muchas veces es útil definir variables de plantilla en vez de simplemente devolver valores. De esta manera, los autores de plantillas podrán directamente utilizar las variables que esta etiqueta defina.
Para definir una variable en el contexto, asignaremos a nuestro objeto
context
disponible en el método render()
nuestras variables, como si de
un diccionario se tratase. Aquí mostramos la versión actualizada de
NodoFechaActual
que define una variable de plantilla, fecha_actual
, en
lugar de devolverla:
class NodoFechaActual2(template.Node): def __init__(self, formato_cadena): self.formato_cadena = str(formato_cadena) def render(self, context): ahora = datetime.datetime.now() context['fecha_actual'] = ahora.strftime(self.formato_cadena) return '
Devolvemos una cadena vacía, debido a que render()
siempre debe devolver
un string. Entonces, si todo lo que la etiqueta hace es definir una variable,
render()
debe al menos devolver una cadena vacía.
De esta manera usaríamos esta nueva versión de nuestra etiqueta:
{% fecha_actual2 "%Y-%M-%d %I:%M %p" %} <p>Fecha:{{ fecha_actual }}.</p>
Pero hay un problema con NodoFechaActual2
: el nombre de la variable
fecha_actual
está definido dentro del código. Esto significa que tendrás que
asegurar que {{ fecha_actual }}
no sea utilizado en otro lugar dentro de la
plantilla, ya que {% fecha_actual %}
sobreescribirá el valor de esa otra
variable.
Una solución más limpia, es recibiendo el nombre de la variable en la etiqueta de plantilla así:
{% traer_fecha_actual "%Y-%M-%d %I:%M %p" as mi_fecha_actual %} <p>Fecha: {{ mi_fecha_actual }}.</p>
Para hacer esto, necesitaremos modificar tanto la función de compilación
como la clase Node
de la siguiente forma:
.. snippet:: :filename: templatetags/etiquetas.py import datetime import re from django import template register = template.Library() class NodoFechaActual3(template.Node): def __init__(self, formato_cadena, var_nombre): self.formato_cadena = str(formato_cadena) self.var_nombre = var_nombre def render(self, context): ahora = datetime.datetime.now() context[self.var_nombre] = ahora.strftime(self.formato_cadena) return '' @register.tag(name="traer_fecha_actual") def traer_hora_actual(parser, token): # Esta version usa expresiones regulares para analizar el contenido de la etiqueta. try: # Dividir por None == dividir por espacios. tag_nombre, arg = token.contents.split(None, 1) except ValueError: msg = '%r La etiqueta requiere un simple argumento' % token.contents[0] raise template.TemplateSyntaxError(msg) m = re.search(r'(.*?) as (\w+)', arg) if m: fmt, var_nombre = m.groups() else: msg = '%r Argumentos no validos para la etiqueta' % tag_nombre raise template.TemplateSyntaxError(msg) if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")): msg = "%r Los argumentos deven de ir entre comillas" % tag_nombre raise template.TemplateSyntaxError(msg) return NodoFechaActual3(fmt[1:-1], var_nombre)
Ahora, traer_fecha_actual()
pasa la cadena de formato junto al nombre de la
variable a NodoFechaActual3
.
Las etiquetas de plantilla pueden funcionar como bloques que contienen otras
etiquetas (piensa en {% if %}
, {% for %}
, etc.). Para crear una
etiqueta como esta, usa parser.parse()
en tu función de compilación.
Aquí vemos como está implementada una etiqueta {% coment %}
:
def do_comment(parser, token):
nodelist = parser.parse(('endcomment',))
parser.delete_first_token()
return CommentNode()
class CommentNode(template.Node):
def render(self, context):
return ''
parser.parse()
toma una tupla de nombres de etiquetas de bloque para
evaluar y devuelve una instancia de django.template.NodeList
, la cual es una
lista de todos los objetos Nodo
que el parser encontró antes de haber
encontrado alguna de las etiquetas nombradas en la tupla.
Entonces, en el ejemplo previo, nodelist
es una lista con todos los nodos
entre {% comment %}
y {% endcomment %}
, excluyendo a los mismos {%
comment %}
y {% endcomment %}
.
Luego de que parser.parse()
es llamado el parser aún no ha "consumido" la
etiqueta {% endcomment %}
, es por eso que en el código se necesita llamar
explícitamente a parser.delete_first_token()
para prevenir que esta
etiqueta sea procesada nuevamente.
Luego, CommentNode.render()
simplemente devuelve un string vacío.
Cualquier cosa entre {% comment %}
y {% endcomment %}
es ignorada.
En el ejemplo anterior, do_comment()
desechó todo entre {% comment %}
y
{% endcomment %}
, pero también es posible hacer algo con el código entre
estas etiquetas.
Por ejemplo, presentamos una etiqueta de plantilla, {% upper %}
, que
convertirá a mayúsculas todo hasta la etiqueta {% endupper %}
:
{% upper %} This will appear in uppercase, {{ your_nombre }}. {% endupper %}
Como en el ejemplo previo, utilizaremos parser.parse()
pero esta vez
pasamos el resultado en nodelist
a Node
:
@register.tag
def do_upper(parser, token):
nodelist = parser.parse(('endupper',))
parser.delete_first_token()
return UpperNode(nodelist)
class UpperNode(template.Node):
def __init__(self, nodelist):
self.nodelist = nodelist
def render(self, context):
output = self.nodelist.render(context)
return output.upper()
El único concepto nuevo aquí es self.nodelist.render(context)
en
UpperNode.render()
. El mismo simplemente llama a render()
en cada
Node
en la lista de nodos.
Para más ejemplos de renderizado complejo, examina el código fuente para las
etiquetas {% if %}
, {% for %}
, {% ifequal %}
y {% ifchanged %}
.
Puedes encontrarlas en django/template/defaulttags.py
.
Muchas etiquetas de plantilla reciben un único argumento--una cadena o una
referencia a una variable de plantilla y retornan una cadena luego de hacer
algún procesamiento basado solamente en el argumento de entrada e información
externa. Por ejemplo la etiqueta fecha_actual
que escribimos antes es de
este tipo. Le pasamos una cadena de formato, y retorna la hora como una cadena.
Para facilitar la creación de esos tipos de etiquetas, Django provee una función
auxiliar: simple_tag
. Esta función, que es un método de
django.template.Library
, recibe una función que acepta un argumento, lo
encapsula en una función render
y el resto de las piezas necesarias que
mencionamos previamente y lo registra con el sistema de plantillas.
Nuestra función fecha_actual
podría entonces ser escrita de la siguiente
manera:
def fecha_actual(format_string):
return datetime.datetime.now().strftime(format_string)
register.simple_tag(fecha_actual)
Y la podemos registrar mediante un decorador asi:
@register.simple_tag def fecha_actual(token): ...
Un par de cosas a tener en cuenta acerca de la función auxiliar simple_tag
:
- Sólo se pasa un argumento a nuestra función.
- La verificación de la cantidad requerida de argumentos ya ha sido realizada para el momento en el que nuestra función es llamada, de manera que no es necesario que lo hagamos nosotros.
- Las comillas alrededor del argumento (si existieran) ya han sido quitadas, de manera que recibimos una cadena común.
Otro tipo de etiquetas de plantilla común es aquel que visualiza ciertos datos renderizando otra plantilla. Por ejemplo la interfaz de administración de Django usa etiquetas de plantillas personalizadas (custom) para visualizar los botones en la parte inferior de la páginas de formularios "agregar/cambiar". Dichos botones siempre se ven igual, pero el destino del enlace cambia dependiendo del objeto que se está modificando. Se trata de un caso perfecto para el uso de una pequeña plantilla que es llenada con detalles del objeto actual.
Ese tipo de etiquetas reciben el nombre de etiquetas de inclusión. Es
probablemente mejor demostrar cómo escribir una usando un ejemplo. Escribamos
una etiqueta que produzca una lista de libros para un simple objeto Libro
.
Usaremos una etiqueta como esta:
{% libros_por_autor autor %}
El resultado será algo como esto:
<ul> <li>Libro uno</li> <li>Libro dos</li> <li>Otro libro</li> </ul>
Primero definimos la función que toma un argumento y produce un diccionario de datos con los resultados. Nota que nos basta un diccionario y no necesitamos retornar nada más complejo. Esto será usado como el contexto para el fragmento de plantilla:
def libros_por_autor(autor): libros = Libro.objects.filter(autores__id=autor.id) return {'libros': libros}
Luego creamos la plantilla usada para renderizar la salida de la etiqueta.:
<ul> {% for libro in libros %} <li>{{ libro.titulo }}</li> {% endfor %} </ul>
Finalmente una vez que la hemos creado, es necesario registrar la etiqueta de
inclusión invocando al método inclusion_tag()
sobre un objeto Library
.
Continuando con nuestro ejemplo, si la plantilla se encuentra en un archivo
llamado libros_por_autor.html
, registraremos la plantilla de la
siguiente manera:
register.inclusion_tag('libros_por_autor.html')(libros_por_autor)
Como siempre, la sintaxis de decoradores de Python 2.4 también funciona, de manera que en cambio podríamos haber escrito:
@register.inclusion_tag('libros_por_autor.html') def libros_por_autor(autor): # ...
A veces tus etiquetas de inclusión necesitan tener acceso a valores del contexto
de la plantilla padre. Para resolver esto Django provee una opción
takes_context
para las etiquetas de inclusión. Si especificas
takes_context
cuando creas una etiqueta de plantilla, la misma no tendrá
argumentos obligatorios y la función Python subyacente tendrá un argumento: el
contexto de la plantilla en el estado en el que se encontraba cuando la etiqueta
fue invocada.
Por ejemplo supongamos que estás escribiendo una etiqueta de inclusión que será
siempre usada en un contexto que contiene variables home_link
y
home_title
que apuntan a la página principal. Así es como se vería la
función Python:
@register.inclusion_tag('link.html', takes_context=True) def jump_link(context): return { 'link': context['home_link'], 'title': context['home_title'], }
Note
El primer parámetro de la función debe llamarse context
.
La plantilla link.html
podría contener lo siguiente:
Jump directly to <a href="{{ link }}">{{ title }}</a>.
Entonces, cada vez que desees usar esa etiqueta personalizada, carga su biblioteca y ejecútala sin argumentos, de la siguiente manera:
{% jump_link %}
Los cargadores de plantillas incluidos con Django (descritos en la sección "Etiquetas de inclusión" más arriba) cubrirán usualmente todas tus necesidades de carga de plantillas, pero es muy sencillo escribir el tuyo propio si necesitas alguna lógica especial en dicha carga. Por ejemplo podrías cargar plantillas desde una base de datos, o directamente desde un repositorio Subversion usando las librerías (bindings) Python de Subversion, o (como veremos) desde un archivo ZIP.
Un cargador de plantillas --esto es, cada entrada en la variables de
configuración TEMPLATE_LOADERS
-- debe ser un objeto invocable (callable)
con la siguiente interfaz:
load_template_source(template_nombre, template_dirs=None)
El argumento template_nombre
es el nombre de la plantilla a cargar (tal como
fue pasado a loader.get_template()
o loader.select_template()
) y
template_dirs
es una lista opcional de directorios en los que se buscará en
lugar de TEMPLATE_DIRS
.
Si un cargador es capaz de cargar en forma exitosa una plantilla, debe retornar
una tupla: (template_source, template_path)
. Donde template_source
es la
cadena de plantilla que será compilada por la maquinaria de plantillas, y
template_path
es la ruta desde la cual fue cargada la plantilla. Dicha ruta
podría ser presentada al usuario para fines de depuración así que debe
identificar en forma rápida desde dónde fue cargada la plantilla.
Si al cargador no le es posible cargar una plantilla, debe lanzar
django.template.TemplateDoesNotExist
.
Cada función del cargador debe también poseer un atributo de función
is_usable
. Este es un Booleano que le informa a la maquinaria de plantillas
si este cargador está disponible en la instalación de Python actual. Por ejemplo
el cargador desde eggs (que es capaz de cargar plantillas desde eggs Python)
fija is_usable
a False
si el módulo pkg_resources
no se encuentra
instalado, porque pkg_resources
es necesario para leer datos desde eggs.
Un ejemplo ayudará a clarificar todo esto. Aquí tenemos una función cargadora de
plantillas que puede cargar plantillas desde un archivo ZIP. Usa una variable
de configuración personalizada TEMPLATE_ZIP_FILES
como una ruta
de búsqueda en lugar de TEMPLATE_DIRS
y espera que cada ítem en dicha ruta
sea un archivo ZIP que contiene plantillas:
import zipfile from django.conf import settings from django.template import TemplateDoesNotExist def load_template_source(template_nombre, template_dirs=None): """Cargador de plantillas desde archivos ZIP.""" template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", []) # Carga cada archivo ZIP de TEMPLATE_ZIP_FILES. for fnombre in template_zipfiles: try: z = zipfile.ZipFile(fnombre) source = z.read(template_nombre) except (IOError, KeyError): continue z.close() # Encuentra las plantillas y retorna el codigo. template_path = "%s:%s" % (fnombre, template_nombre) return (source, template_path) # Si en este punto la plantilla no ha sido cargada, lanzamos un error. # TemplateDoesNotExist(template_nombre) # Este cargador siempre es usable(ya que zipfile esta incluido en Python) load_template_source.is_usable = True
El único paso restante si deseamos usar este cargador es agregarlo a la variable
de configuración TEMPLATE_LOADERS
. Si pusiéramos este código en un paquete
llamado misitio.zip_loader
entonces agregaremos
misitio.zip_loader.load_template_source
a TEMPLATE_LOADERS
.
La interfaz de administración de Django incluye una referencia completa de todas las etiquetas y filtros de plantillas disponibles para un sitio determinado. Está designada para ser una herramienta que los programadores Django proveen a los desarrolladores de plantillas. Para verla, ve a la interfaz de administración y haz click en el enlace Documentación en la zona superior derecha de la página.
La referencia está dividida en cuatro secciones: etiquetas, filtros, modelos y vistas. Las secciones etiquetas y filtros describen todas las etiquetas incluidas (en efecto, las referencias de etiquetas y filtros del :doc:`capítulo 4<chapter04>` han sido extraídas directamente de esas páginas) así como cualquier biblioteca de etiquetas o filtros personalizados disponible.
La página views es la más valiosa. Cada URL en tu sitio tiene allí una entrada separada. Si la vista relacionada incluye una docstring, haciendo click en la URL te mostrará lo siguiente:
- El nombre de la función de vista que genera esa vista.
- Una breve descripción de qué hace la vista.
- El contexto, o una lista de variables disponibles en la plantilla de la vista.
- El nombre de la plantilla o plantillas usados para esa vista.
Para un ejemplo detallado de la documentación de vistas, lee el código fuente de
la vista genérica de Django object_list
la cual se encuentra en
django/views/generic/list_detail.py
.
Debido a que los sitios implementados con Django generalmente usan objetos de bases de datos, las páginas models describen cada tipo de objeto en el sistema así como todos los campos disponibles en esos objetos.
En forma conjunta, las páginas de documentación deberían proveerte cada etiqueta, filtro, variable y objeto disponible para su uso en una plantilla arbitraria.
Nota:
Estas sección es sólo de interés para aquellos que intentan usar el sistema de plantillas como un componente de salida en otra aplicación. Si estás usando el sistema como parte de un aplicación Django, la información aquí presentada no es relevante para ti.
Normalmente Django carga toda la información de configuración que necesita desde
su propio archivo de configuración por omisión, combinado con las variables de
configuración en el módulo indicado en la variable de entorno
DJANGO_SETTINGS_MODULE
. Pero si estás usando el sistema de plantillas
independientemente del resto de Django, el esquema de la variable de entorno no
es muy conveniente porque probablemente quieras configurar el sistema de
plantillas en una manera mas acorde con el resto de tu aplicación en lugar de tener
que vértelas con archivos de configuración e indicando los mismos con variables
de entorno.
Para resolver este problema necesitas usar la opción de configuración manual
descrita en forma completa en el Apéndice E. En resumen, necesitas importar las
partes apropiadas del sistema de plantillas y entonces, antes de invocar
alguna de las funciones de plantillas, invoca a
django.conf.settings.configure()
con cualquier valor de configuración que
desees especificar.
Podrías desear considerar fijar al menos TEMPLATE_DIRS
(si vas a usar
cargadores de plantillas), DEFAULT_CHARSET
(aunque el valor por omisión
utf-8
probablemente sea adecuado) y TEMPLATE_DEBUG
. Todas las variables
de configuración están descritas en el Apéndice E y todos las variables cuyos
nombres comienzan con TEMPLATE
son de obvio interés.
Continuamos esta sección con temas avanzados, el :doc:`siguiente capítulo<chapter10>` trata sobre el uso avanzado de los modelos en Django.