幕后的ListView 执行大量操作来创建上下文并将其传递给渲染引擎。我们可以通过Classy Class-Based Views看看实现。
本质上,当您触发此类基于类的视图时,您将根据 HTTP 方法触发get(..)、post(..) 等方法。
get(..)方法由BaseListView类定义,定义为:
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)
导入部分是我们先将get_queryset()的结果转化为self.objects_list,然后用self.get_context_data()构造一个上下文。然后我们调用self.render_to_response(..),它基本上将使用指定的模板,并使用给定的context 渲染它。
get_context 数据有两个父对象,它们都有一个实现。最基本的(继承层次中最高的)是ContextMixin,但这个函数并没有做太多:
def get_context_data(self, **kwargs):
kwargs.setdefault('view', self)
if self.extra_context is not None:
kwargs.update(self.extra_context)
return kwargs
它只接受由关键字参数构造的字典(如果没有关键字参数,则为空,这里就是这种情况),并添加一个与self 关联的额外键'view'。它还可以添加可以在self.extra_context 中定义的额外键值对,但我们可以在这里忽略它。
最有趣的逻辑是在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)
这里发生的情况是,我们首先将self.object_list(我们首先使用self.get_queryset 的结果设置的变量)分配给名为queryset 的局部变量。然后,我们将对 queryset 进行分页,但这与您的问题不太相关。
然后我们通过调用self.get_context_object_name(queryset) 来获取名称。默认情况下,这实现为:
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
因此,如果您设置了 context_object_name 属性,就像您所做的那样,那么它将简单地返回该名称。因此我们可以得出结论,在get_context_data(..), 方法中,context_object_name 将具有您所提供的名称,这里是'latest_question_list'。
然后我们继续处理get_context_data(..) 中的代码:我们构造一个字典,并在底部检查context_object_name 是否不是None。如果是这种情况,我们将queryset 与该键相关联(因此这里与'latest_question_list' 相关联)。最终,当构建正确的上下文字典时,我们使用构建的上下文为**kwargs 进行super() 调用,正如我们之前讨论的,ContextMixin 将简单地返回该字典并进行非常小的更改。
因此,最后context 将具有与queryset 关联的列表名称(此处为'latest_question_list'),并且它将使用该上下文数据呈现模板。