Ver índice de contenidos del libro

10.2. Procesadores de contexto

Cuando una plantilla debe ser renderizada, necesita un contexto. Normalmente 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.

Usa RequestContext cuando no quieras especificar el mismo conjunto de variables una y otra vez en una serie de plantillas. Por ejemplo, considera estas cuatro vistas:

from django.template import loader, Context
 
def view_1(request):
    # ...
    t = loader.get_template('template1.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am view 1.'
    })
    return t.render(c)
 
def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the second view.'
    })
    return t.render(c)
 
def view_3(request):
# ...
    t = loader.get_template('template3.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the third view.'
    })
    return t.render(c)
 
def view_4(request):
    # ...
    t = loader.get_template('template4.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the fourth view.'
    })
    return t.render(c)

A propósito no hemos usado el atajo render_to_response 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 — app, user y ip_address — a su plantilla. ¿No sería bueno poder eliminar esa redundancia?

RequestContext y los procesadores de contexto 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_to_response(). El secreto está en utilizar RequestContext en lugar de Context cuando renderizamos 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):
    "A context processor that provides 'app', 'user' and 'ip_address'."
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }
 
def view_1(request):
    # ...
    t = loader.get_template('template1.html')
    c = RequestContext(request, {'message': 'I am view 1.'},
            processors=[custom_proc])
    return t.render(c)
 
def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = RequestContext(request, {'message': 'I am the second view.'},
            processors=[custom_proc])
    return t.render(c)
 
def view_3(request):
    # ...
    t = loader.get_template('template3.html')
    c = RequestContext(request, {'message': 'I am the third view.'},
            processors=[custom_proc])
    return t.render(c)
 
def view_4(request):
    # ...
    t = loader.get_template('template4.html')
    c = RequestContext(request, {'message': 'I am the fourth view.'},
            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 objeto HttpRequest y devuelve un diccionario con variables a usar en el contexto de la plantilla. Eso es todo lo que hace.
  • Hemos cambiado las cuatro vistas para que usen RequestContext en lugar de Context. Hay dos diferencias en cuanto a cómo el contexto es construido. Uno, RequestContext requiere que el primer argumento sea una instancia de HttpRequest — la cual fue pasada a la vista en primer lugar (request). Dos, RequestContext recibe un parámetro opcional processors, el cual es una lista o tupla de funciones procesadoras de contexto a utilizar. En este caso, pasamos custom_proc, nuestro procesador de contexto definido previamente.
  • Ya no es necesario en cada vista incluir app, user o ip_address cuando construimos el contexto, ya que ahora estas variables son provistas por custom_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 message es creada de manera diferente en cada una de las vistas.

En el Capítulo 4, presentamos el atajo render_to_response(), el cual nos ahorra tener que llamar a loader.get_template(), luego crear un Context y ademas, 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_to_response(), pero es posible — y preferible — utilizar los procesadores de contexto junto a render_to_response(). Esto lo logramos mediante el argumento context_instance de la siguiente manera:

from django.shortcuts import render_to_response
from django.template import RequestContext
 
def custom_proc(request):
    "A context processor that provides 'app', 'user' and 'ip_address'."
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }
 
def view_1(request):
    # ...
    return render_to_response('template1.html',
        {'message': 'I am view 1.'},
        context_instance=RequestContext(request, processors=[custom_proc]))
 
def view_2(request):
    # ...
    return render_to_response('template2.html',
        {'message': 'I am the second view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))
 
def view_3(request):
    # ...
    return render_to_response('template3.html',
        {'message': 'I am the third view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))
 
def view_4(request):
    # ...
    return render_to_response('template4.html',
        {'message': 'I am the fourth view.'},
        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 strings, 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.

10.2.1.  django.core.context_processors.auth

Si TEMPLATE_CONTEXT_PROCESSORS contiene este procesador, cada RequestContext contendrá las siguientes variables:

  • user: Una instancia de django.contrib.auth.models.User representando al usuario actualmente autenticado (o una instancia de AnonymousUser 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 a request.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 de django.core.context_processors.PermWrapper, la cual representa los permisos que posee el usuario actualmente autenticado.

En el Capítulo 12 encontrarás más información acerca de usuarios, permisos y mensajes.

10.2.2. django.core.context_processors.debug

Este procesador añade 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ón DEBUG (True o False). 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.

10.2.3. django.core.context_processors.i18n

Si este procesador está habilitado, cada RequestContext contendrá las siguientes variables:

  • LANGUAGES: El valor del parámetro de configuración LANGUAGES.
  • LANGUAGE_CODE: request.LANGUAGE_CODE si existe; de lo contrario, el valor del parámetro de configuración LANGUAGE_CODE.

En el Apéndice E se especifica más información sobre estos parámetros.

10.2.4. django.core.context_processors.request

Si este procesador está habilitado, cada RequestContext contendrá una variable request, la cual es el actual objeto HttpRequest. Este procesador no está habilitado por defecto.

10.2.5. Consideraciones para escribir tus propios procesadores de contexto

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 llamado context_processors.py ubicado en tu aplicación o en tu proyecto.