El libro de Django 1.0

4.6. Cargadores de plantillas

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 directorio mysite 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.

La página de error que se muestra cuando una plantilla no se encuentra

Figura 4.1 La página de error que se muestra cuando una plantilla no se encuentra

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, o HttpResponse. En vez de esto, importamos django.shortcuts.render_to_response. import datetime se mantiene.
  • En la función current_datetime, seguimos calculando now, pero la carga de la plantilla, creación del contexto, renderización de esta, y de la creación de HttpResponse se encarga la llamada a render_to_response(). Como render_to_response() retorna un objeto HttpResponse, podemos simplemente retornar 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 es True, verás la excepción TemplateDoesNotExist sobre la página de error de Django.
  • Si DEBUG es False, la etiqueta fallará silenciosamente, sin mostrar nada en el lugar de la etiqueta.