Estoy un poco confundido con las vistas genéricas de Django. Como se muestra en aquí estamos convirtiendo vistas personalizadas en genéricas puntos de vista. Y aunque entiendo lo que sucede en DetailView y ResultsView, no entiendo completamente cómo esto:
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)
Se convierte en esto:
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
- En el primer ejemplo,
latest_question_list = Question.objects.order_by('-pub_date')[:5]
- Pero en el segundo ejemplo, ¿a qué variable
latest_question_list
es igual aquí? Ni siquiera lo hemos definido ...
¿Alguien puede arrojar algo de luz sobre esto?
3 respuestas
Un ListView
detrás de las cortinas realiza muchas operaciones para crear un contexto y pasarlo a un motor de renderizado. Podemos echar un vistazo a la implementación a través de Vistas con clase con clase .
En esencia, cuando active dicha vista basada en clases, según el método HTTP, activará el método get(..)
, post(..)
, etc.
El método get(..)
está definido por la clase BaseListView
y se define como:
def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty: # When pagination is enabled and object_list is a queryset, # it's better to do a cheap query than to load the unpaginated # queryset in memory. if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'): is_empty = not self.object_list.exists() else: is_empty = not self.object_list if is_empty: raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % { 'class_name': self.__class__.__name__, }) context = self.get_context_data() return self.render_to_response(context)
La parte de importación es que primero somos el resultado de get_queryset()
a self.objects_list
, y luego construimos un contexto con self.get_context_data()
. Luego hacemos una llamada a self.render_to_response(..)
, que básicamente usará la plantilla especificada y la representaremos con el context
dado.
Los datos get_context
tienen dos padres con una implementación. El más básico (el más alto en la jerarquía de herencia) es el de ContextMixin
, pero esta función no hace mucho:
def get_context_data(self, **kwargs): kwargs.setdefault('view', self) if self.extra_context is not None: kwargs.update(self.extra_context) return kwargs
Simplemente toma el diccionario construido por los argumentos de las palabras clave (vacío si no hay argumentos de palabras clave, como es el caso aquí), y agrega una clave adicional 'view'
que está asociada con self
. También puede agregar pares clave-valor adicionales que se pueden definir en self.extra_context
, pero podemos ignorar eso aquí.
La lógica más interesante se implementa en MultipleObjectMixin
:
def get_context_data(self, *, object_list=None, **kwargs): """Get the context for this view.""" queryset = object_list if object_list is not None else self.object_list page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size) context = { 'paginator': paginator, 'page_obj': page, 'is_paginated': is_paginated, 'object_list': queryset } else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super().get_context_data(**context)
Lo que sucede aquí es que primero asignamos self.object_list
, la variable que primero hemos establecido con el resultado de self.get_queryset
a una variable local llamada queryset
. Luego paginaremos eso queryset
, pero eso no es muy relevante para su pregunta.
Luego obtenemos el nombre llamando self.get_context_object_name(queryset)
. Por defecto esto se implementa como:
def get_context_object_name(self, object_list): """Get the name of the item to be used in the context.""" if self.context_object_name: return self.context_object_name elif hasattr(object_list, 'model'): return '%s_list' % object_list.model._meta.model_name else: return None
Entonces, si ha establecido el atributo context_object_name
, como lo hizo, simplemente devolverá ese nombre. Por lo tanto, podemos concluir que en el método get_context_data(..)
, el context_object_name
tendrá el nombre que le dio, aquí 'latest_question_list'
.
Seguimos procesando el código en get_context_data(..)
: construimos un diccionario y en la parte inferior verificamos si context_object_name
no es None
. Si ese es el caso, asociamos el queryset
con esa clave (así que aquí con 'latest_question_list'
). Eventualmente, cuando se construye el diccionario de contexto correcto, realizamos una llamada super()
con el contexto construido como **kwargs
, y como discutimos antes, el ContextMixin
, simplemente devolverá ese diccionario con muy pequeño cambios
Entonces, al final, context
tendrá el nombre de su lista (aquí 'latest_question_list'
) asociado con queryset
, y mostrará la plantilla con los datos de contexto.
En la vista basada en clase usaste context_object_name = 'latest_question_list'
Por eso es similar a latest_question_list
, que usaste en la vista basada en funciones.
En la vista basada en clases, si no agrega context_object_name
, entonces su valor es automático object_list
. Algo así como context_object_name='object_list'
.
TL; versión DR de la respuesta aceptada.
Bajo el capó, Django hace muchas cosas en la vista genérica ListView.
Para tal vista:
class IndexView(generic.ListView):
model=Question
La variable de contexto generada automáticamente es question_list. Si desea anularlo, debe usar la variable context_object_name
para establecer un nombre para su variable de contexto personalizada. Eso es todo, solo establecer un nombre.
Luego debe usar la función predefinida get_queryset
, que relacionará su salida con la variable context_object_name
.
Por lo tanto, es importante utilizar estos nombres particulares para la variable y la función.
Preguntas relacionadas
Nuevas preguntas
django
Django es un marco de aplicación web del lado del servidor de código abierto escrito en Python. Está diseñado para reducir el esfuerzo requerido para crear sitios web y aplicaciones web complejos basados en datos, con un enfoque especial en menos código, sin redundancia y siendo más explícito que implícito.