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 objetoHttpRequest
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 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 tupla de funciones procesadoras de contexto a utilizar. En este caso, pasamoscustom_proc
, nuestro procesador de contexto definido previamente. - Ya no es necesario en cada vista incluir
app
,user
oip_address
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
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 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 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ó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
.
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ónLANGUAGES
.LANGUAGE_CODE
:request.LANGUAGE_CODE
si existe; de lo contrario, el valor del parámetro de configuraciónLANGUAGE_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 llamadocontext_processors.py
ubicado en tu aplicación o en tu proyecto.