Django provee una práctica y poderosa API para cargar plantillas del disco, con el objetivo de quitar la redundancia en la carga de la plantilla y en las mismas plantillas.
Para usar la API para cargar plantillas, primero necesitas indicarle al framework dónde están guardadas tus plantillas. El lugar para hacer esto es en el archivo de configuración.
El archivo de configuración de Django es el lugar para poner configuraciones para tu instancia o proyecto de Django. Es un simple módulo de Python con variables, una por cada configuración.
Cuando ejecutaste django-admin.py startproject mysite
en el Capítulo 2,
el script creó un archivo de configuración por omisión por ti, llamado
settings.py
. Échale un vistazo al contenido del archivo. Este contiene
variables que se parecen a estas (no necesariamente en este orden):
- DEBUG = True
- TIME_ZONE = 'America/Chicago'
- USE_I18N = True
- ROOT_URLCONF = 'mysite.urls'
Éstas se explican por sí solas; las configuraciones y sus respectivos valores son simples variables de Python. Como el archivo de configuración es sólo un módulo plano de Python, puedes hacer cosas dinámicas como verificar el valor de una variable antes de configurar otra. (Esto también significa que debes evitar errores de sintaxis de Python en los archivos de configuración).
Cubriremos el archivo de configuración en profundidad en el Apéndice E, pero
por ahora, veamos la variable de configuración TEMPLATE_DIRS
. Esta variable
le indica al mecanismo de carga de plantillas dónde buscar las plantillas. Por
omisión, ésta es una tupla vacía. Elige un directorio en el que desees guardar
tus plantillas y agrega este a TEMPLATE_DIRS
, así:
TEMPLATE_DIRS = ('/home/django/mysite/templates',)
Hay algunas cosas para notar:
- Puedes especificar cualquier directorio que quieras, siempre y cuando la
cuenta de usuario en el cual se ejecuta el servidor web tengan acceso al
directorio y su contenido. Si no puedes pensar en un lugar apropiado para
poner las plantillas, te recomendamos crear un directorio
templates
dentro del proyecto de Django (esto es, dentro del directoriomysite
que creaste en el Capítulo 2 , si vienes siguiendo los ejemplos a lo largo del libro). - ¡No olvides la coma al final del string del directorio de plantillas! Python requiere una coma en las tuplas de un solo elemento para diferenciarlas de una expresión de paréntesis. Esto es común en los usuarios nuevos.
Si quieres evitar este error, puedes hacer `TEMPLATE_DIRS` una lista, en vez de una tupla, porque un solo elemento en una lista no requiere estar seguido de una coma:
TEMPLATE_DIRS = ['/home/django/mysite/templates']
- Una tupla es un poco más correcta semánticamente que una lista (las
tuplas no pueden cambiar luego de ser creadas, y nada podría cambiar las
configuraciones una vez que fueron leídas), nosotros recomendamos usar
tuplas para la variable
TEMPLATE_DIRS
. - Si estás en Windows, incluye tu letra de unidad y usa el estilo de Unix para las barras en vez de barras invertidas, como sigue::
TEMPLATE_DIRS = ('C:/www/django/templates',)
- Es más sencillo usar rutas absolutas (esto es, las rutas de directorios
comienzan desde la raíz del sistema de archivos). Si quieres ser un poco
más flexible e independiente, también, puedes tomar el hecho de que el
archivo de configuración de Django es sólo código de Python y construir la
variable
TEMPLATE_DIRS
dinámicamente.
Este ejemplo usa la variable de Python "mágica" `__file__`, la cual es automáticamente asignada al nombre del archivo del módulo de Python en el que se encuentra el código.
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
Con la variable TEMPLATE_DIRS
configurada, el próximo paso es cambiar el
código de vista que usa la funcionalidad de carga de plantillas de Django,
para no incluir la ruta a la plantilla. Volvamos a nuestra vista
current_datetime
, vamos a cambiar esta como sigue:
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
En este ejemplo, usamos la función django.template.loader.get_template()
en vez de cargar la plantilla desde el sistemas de archivos manualmente. La
función get_template()
toma el nombre de la plantilla como argumento,
se da cuenta de dónde está la plantilla en el sistema de archivos, lo abre, y
retorna un objeto Template
compilado.
Si get_template()
no puede encontrar la plantilla con el nombre pasado, esta
levanta una excepción TemplateDoesNotExist
. Para ver que cómo se ve eso,
ejecutar el servidor de desarrollo de Django otra vez, como en el Capítulo 3,
ejecutando python manage.py runserver
en el directorio de tu proyecto de
Django. Luego, escribe en tu navegador la página que activa la vista
current_datetime
(o sea, http://127.0.0.1:8000/time/
). Asumiendo que tu
variable de configuración DEBUG
está asignada a True
y todavía no has
creado la plantilla current_datetime.html
, deberías ver una página de error
de Django resaltando el error TemplateDoesNotExist
.
Esta página de error es similar a la que explicamos en el Capítulo 3, con una pieza adicional de información de depuración: una sección "Postmortem del cargador de plantillas". Esta sección te indica qué plantilla intentó cargar Django acompañado de una razón para cada intento fallido (por ej. "File does not exist"). Esta información es invaluable cuando hacemos depuración de errores de carga de plantillas.
Como probablemente puedas distinguir de los mensajes de error de la Figura 4-1,
Django intentó buscar una plantilla combinando el directorio de la variable
TEMPLATE_DIRS
con el nombre de la plantilla pasada a get_template()
.
Entonces si tu variable TEMPLATE_DIRS
contiene '/home/django/templates'
,
Django buscará '/home/django/templates/current_datetime.html'
. Si
TEMPLATE_DIRS
contiene más que un directorio, cada uno de estos es
examinado hasta que se encuentre la plantilla o hasta que no haya más
directorios.
Continuando, crea el archivo current_datetime.html
en tu directorio de
plantillas usando el siguiente código:
<html><body>It is now {{ current_date }}.</body></html>
Refresca la página en tu navegador web, y deberías ver la página completamente renderizada.
4.6.1. render_to_response()
Debido a que es común cargar una plantilla, rellenar un Context
, y retornar
un objeto HttpResponse
con el resultado de la plantilla renderizada, Django
provee un atajo que te deja hacer estas cosas en una línea de código. Este
atajo es la función llamada render_to_response()
, la cual se encuentra en el
módulo django.shortcuts
. La mayoría de las veces, usarás
render_to_response()
en vez de cargar las plantillas y crear los objetos
Context
y HttpResponse
manualmente.
Aquí está el ejemplo actual current_datetime
reescrito utilizando
render_to_response()
:
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
¡Qué diferencia! Vamos paso a paso a través de los cambios del código:
- No tenemos que importar
get_template
,Template
,Context
, oHttpResponse
. En vez de esto, importamosdjango.shortcuts.render_to_response
.import datetime
se mantiene. - En la función
current_datetime
, seguimos calculandonow
, pero la carga de la plantilla, creación del contexto, renderización de esta, y de la creación deHttpResponse
se encarga la llamada arender_to_response()
. Comorender_to_response()
retorna un objetoHttpResponse
, podemos simplementeretornar
ese valor en la vista.
El primer argumento de render_to_response()
debe ser el nombre de la
plantilla a utilizar. El segundo argumento, si es pasado, debe ser un
diccionario para usar en la creación de un Context
para esa plantilla. Si
no se le pasa un segundo argumento, render_to_response()
utilizará un
diccionario vacío.
4.6.2. El truco locals()
Considera nuestra última versión de current_datetime
:
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
Muchas veces, como en este ejemplo, buscarás tú mismo calcular algunos valores,
guardando ellos en variables (por ej. now
en el código anterior), y pasando
estas a la plantilla. Particularmente los programadores perezosos notarán que
es ligeramente redundante tener esos nombres en variables temporales y
tener nombres para las variables de la plantilla. No sólo que esto es
redundante, sino que también hay que teclear más.
Entonces si eres uno de esos programadores perezosos y quieres ahorrar código
particularmente conciso, puedes tomar la ventaja de la función built-in de
Python llamada locals()
. Esta retorna un diccionario mapeando todos los
nombres de variables locales con sus valores. De esta manera, la vista anterior
podría reescribirse como sigue:
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
Aquí, en vez de especificar manualmente el diccionario al contexto como antes,
pasamos el valor de locals()
, el cual incluye todas las variables definidas
hasta ese punto en la ejecución de la función. Como una consecuencia,
renombramos el nombre de la variable now
a current_date
, porque esta es
la variable que especificamos en la plantilla. En este ejemplo, locals()
no
ofrece una gran mejora, pero esta técnica puede ahorrar un poco de tipeo si
tienes plantillas con varias variables definidas — o si eres perezoso.
Una cosa en la que tiene que tener cuidado cuando usas locals()
es que esta
incluye todas las variables locales, con lo cual quizás conste de más
variables de las cuales quieres tener acceso en la plantilla. En el ejemplo
anterior, locals()
también incluirá request
. Depende de tu aplicación
saber si esto es de importancia.
La última cosa a considerar es que locals()
provoca un poco sobrecarga,
porque cuando es llamado, Python crea el diccionario dinámicamente. Si
especificas el diccionario al contexto manualmente, evitas esta sobrecarga.
4.6.3. Subdirectorios en get_template()
Puede ser un poco inmanejable guardar todas las plantillas en un solo directorio. Quizás quieras guardar las plantillas en subdirectorios del directorio de tus plantillas, y esto está bien. De hecho, recomendamos hacerlo; algunas de las características más avanzadas de Django (como las vistas genéricas del sistema, las cuales veremos en el Capítulo 9) esperan esta distribución de las plantillas como una convención por omisión.
Guardar las plantillas en subdirectorios de tu directorio de plantilla es
fácil. En tus llamadas a get_template()
, sólo incluye el nombre del
subdirectorio y una barra antes del nombre de la plantilla, así:
t = get_template('dateapp/current_datetime.html')
Debido a que render_to_response()
es un pequeño envoltorio de
get_template()
, puedes hacer lo mismo con el primer argumento de
render_to_response()
.
No hay límites para la profundidad del árbol de subdirectorios. Siéntete libre de usar tantos como quieras.
Nota Los usuario de Windows, asegúrense de usar barras comunes en vez de barras
invertidas. get_template()
asume el estilo de designación de archivos
de Unix.
4.6.4. La etiqueta de plantilla include
Ahora que vimos el mecanismo para cargar plantillas, podemos introducir una
etiqueta para simplificar esto: {% include %}
. Esta etiqueta te permite
incluir el contenido de otra plantilla. El argumento para esta etiqueta
debería ser el nombre de la plantilla a incluir, y el nombre de
la plantilla también se puede indicar mediante una variable cuyo valor sea una
cadena de texto encerrada con comillas simples o dobles. En cualquier momento
que tengas el mismo código en varias etiquetas, considera utilizar un
{% include %}
para eliminar lo duplicado.
Estos dos ejemplos incluyen el contenido de la plantilla nav.html
. Los
ejemplos son equivalentes e ilustran que cualquier modo de comillas está
permitido:
{% include 'nav.html' %}
{% include "nav.html" %}
Este ejemplo incluye el contenido de la plantilla includes/nav.html
:
{% include 'includes/nav.html' %}
Este ejemplo incluye el contenido de la plantilla cuyo nombre se encuentra en
la variable template_name
:
{% include template_name %}
Como en get_template()
, el nombre del archivo de la plantilla es
determinado agregando el directorio de plantillas tomado de TEMPLATE_DIRS
para el nombre de plantilla solicitado.
Las plantillas incluidas son evaluadas con el contexto de la plantilla en la cual está incluida.
Si una plantilla no encuentra el nombre tomado, Django hará una de estas dos cosas:
- Si
DEBUG
esTrue
, verás la excepciónTemplateDoesNotExist
sobre la página de error de Django. - Si
DEBUG
esFalse
, la etiqueta fallará silenciosamente, sin mostrar nada en el lugar de la etiqueta.