Django 教程。通用视图。 context_object_name = 'latest_question_list'

Posted

技术标签:

【中文标题】Django 教程。通用视图。 context_object_name = \'latest_question_list\'【英文标题】:Django tutorial. Generic views. context_object_name = 'latest_question_list'Django 教程。通用视图。 context_object_name = 'latest_question_list' 【发布时间】:2019-05-10 16:54:20 【问题描述】:

我对 Django 通用视图有点困惑。如here 所示,我们正在将自定义视图转换为通用视图。虽然我了解 DetailView 和 ResultsView 中发生的情况,但我并不完全理解这是怎么回事:

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)

转换成这个:

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]
    在第一个示例中,latest_question_list = Question.objects.order_by('-pub_date')[:5] 但在第二个例子中,latest_question_list 变量在这里等于什么?我们甚至还没有定义它..

任何人都可以对此有所了解吗?

【问题讨论】:

好的,我知道了。供日后参考。在我们的 index.html 模板中,我们使用了:% for question in latest_question_list % question_list,并指定我们自己的 latest_question_list Here's a YT video 上的 Django 泛型可以解决这个问题。即使没有这个视频项目的上下文,项目设置也很小,与官方的 Django 3 教程非常相似。我发现它比接受的答案更有用,如果不是至少支持它的话。 【参考方案1】:

幕后的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'),并且它将使用该上下文数据呈现模板。

【讨论】:

【参考方案2】:

在基于类的视图中,您使用了context_object_name = 'latest_question_list' 这就是为什么它类似于latest_question_list,你在基于函数的视图中使用。

在基于类的视图中,如果您不添加context_object_name,那么它的值会自动为object_list。 类似于context_object_name='object_list'

【讨论】:

【参考方案3】:

TL;已接受答案的 DR 版本。

在底层,Django 在 ListView 通用视图中做了很多事情。

对于这样的观点:

class IndexView(generic.ListView):
      model=Question

自动生成的上下文变量是 question_list。 如果要覆盖它,则必须使用 context_object_name 变量为自定义上下文变量设置名称。就是这样,只是设置一个名称。

然后您必须使用get_queryset 预定义函数,它将其输出与context_object_name 变量相关联。

因此,为变量和函数使用这些特定名称很重要。

【讨论】:

以上是关于Django 教程。通用视图。 context_object_name = 'latest_question_list'的主要内容,如果未能解决你的问题,请参考以下文章

来自django教程的通用视图UpdateView不保存文件或图像

urls.py 中的 Django 时区逻辑和通用视图引发错误,但它是视图、字段还是我?

使用通用登录视图时出现“名称'django'未定义”错误?

Django 中的自定义与通用视图

基于 Django 类的视图和通用视图详细信息使用

基于 Django 类的通用视图和 ModelForms