No hay duda de que usar las vistas genéricas puede acelerar el desarrollo sustancialmente. En la mayoría de los proyectos, sin embargo, llega un momento en el que las vistas genéricas no son suficientes. De hecho, la pregunta más común que se hacen los nuevos desarrolladores de Django es cómo hacer que las vistas genéricas manejen un rango más amplio de situaciones.
Afortunadamente, en casi cada uno de estos casos, hay maneras de simplemente extender las vistas genéricas para manejar un conjunto más amplio de casos de uso. Estas situaciones normalmente corresponden a varios patrones comunes que se explican en las próximas secciones.
9.3.1. Crear contextos de plantilla personalizados
Tal vez hayas notado que el ejemplo de la plantilla publisher list almacena
todos los books en una variable llamada object_list
. Aunque que esto
funciona bien, no es algo sencillo para los autores de plantillas: ellos
sólo tienen que "saber" aquí que están trabajando con books. Un nombre mejor
para esa variable sería publisher_list
; el contenido de esa variable es
bastante obvio.
Podemos cambiar el nombre de esa variable fácilmente con el argumento
template_object_name
:
publisher_info = {
"queryset" : Publisher.objects.all(),
"template_object_name" : "publisher",
}
urlpatterns = patterns('',
(r'^publishers/$', list_detail.object_list, publisher_info)
)
Proveer un template_object_name
útil es siempre una buena idea. Tus
compañeros de trabajo que diseñan las plantillas te lo agradecerán.
9.3.2. Agregar un contexto extra
A menudo simplemente necesitas presentar alguna información extra aparte de la
proporcionada por la vista genérica. Por ejemplo, piensa en mostrar una lista
de todos los otros publisher en cada página de detalle de un publisher. La
vista genérica object_detail
provee el publisher al contexto, pero parece
que no hay forma de obtener una lista de todos los publishers en esa
plantilla.
Pero sí la hay: todas las vistas genéricas toman un parámetro opcional extra,
extra_context
. Este es un diccionario de objetos extra que serán agregados
al contexto de la plantilla. Por lo tanto, para proporcionar la lista de todos
los publishers en la vista de detalles, usamos un diccionario info como el que
sigue:
publisher_info = {
"queryset" : Publisher.objects.all(),
"template_object_name" : "publisher",
"extra_context" : {"book_list" : Book.objects.all()}
}
Esto llenaría una variable {{ book_list }}
en el contexto de la plantilla.
Este patrón puede ser usado para pasar cualquier información hacia la plantilla
para la vista genérica. Es muy práctico.
Sin embargo, en realidad hay un error sutil aquí — ¿puedes detectarlo?
El problema aparece cuando las consultas en extra_context
son evaluadas.
Debido a que este ejemplo coloca Publisher.objects.all()
en la URLconf, sólo
se evaluará una vez (cuando la URLconf se cargue por primera vez). Una vez que
agregues o elimines publishers, notarás que la vista genérica no refleja estos
cambios hasta que reinicias el servidor Web (mira "Almacenamiento en caché y
QuerySets" en el Apéndice C para mayor información sobre cuándo los QuerySets
son almacenados en la cache y evaluados).
Este problema no se aplica al argumento queryset
de las vistas
genéricas. Ya que Django sabe que ese QuerySet en particular nunca debe
ser almacenado en la caché, la vista genérica se hace cargo de limpiar la
caché cuando cada vista es renderizada.
La solución es usar un callback en extra_context
en vez de un valor.
Cualquier callable (por ejemplo, una función) que sea pasado a
extra_context
será evaluado cuando su vista sea renderizada (en vez de sólo
la primera vez). Puedes hacer esto con una función explícitamente definida:
def get_books():
return Book.objects.all()
publisher_info = {
"queryset" : Publisher.objects.all(),
"template_object_name" : "publisher",
"extra_context" : **{"book_list" : get_books}**
}
o puedes usar una versión menos obvia pero más corta que se basa en el hecho de
que Publisher.objects.all
es en sí un callable:
publisher_info = {p
"queryset" : Publisher.objects.all(),
"template_object_name" : "publisher",
"extra_context" : **{"book_list" : Book.objects.all}**
}
Nota la falta de paréntesis después de Book.objects.all
; esto hace
referencia a la función sin invocarla realmente (cosa que hará la vista
genérica luego).