Django 详细视图 get_queryset 和 get_object

Django detailview get_queryset and get_object

我正在使用 Django detailview。最初,我使用 URL 模式

url(r'^todo/details/(?P<pk>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),

我的观点是

class todoDetailView(DetailView):
model = models.todo

它运行良好。

第二种情况,我的URL是

url(r'^todo/details/(?P<id>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),

这次我修改了我的观点

class todoDetailView(DetailView):
model = models.todo
# context_object_name = 'todo_detail'

 def get_object(self, **kwargs):
    print(kwargs)
    return models.todo.objects.get(id=self.kwargs['id'])

没问题,我把第二种情况改成了

class todoDetailView(DetailView):
model = models.todo
# context_object_name = 'todo_detail'

def get_queryset(self):
    return models.todo.objects.get(id=self.kwargs['id'])

然后我得到一个错误,

Generic detail view todoDetailView must be called with either an object pk or a slug.

我知道没有提供合适的 slug 或 pk。所以,最初我添加了 get_object() (它起作用了)但是 get_queryset() 不起作用。他们的工作有什么不同??

而且如果用户仅根据 slug 获取详细信息,我在 Whosebug 上读到

这个可以用

slug_field = 'param_name'
slug_url_kwarg = 'param_name'

link - Generic detail view ProfileView must be called with either an object pk or a slug

任何人都可以向我解释 get_object() 和 get_queryset() 的实际工作原理(如果可能,还有 get_slug_field())

连同术语 slug_fieldslug_url_kwarg

提前致谢

我无法帮助您了解错误消息的明确含义,但是 get_queryset 在列表视图中用于获取多个对象,而 get_object 用于获取单个对象(即 DetailView).

如果您有可用于获取对象的 pk,则无需指定 slug 字段。当您没有或不能公开显示主键时,Slug 字段用于过滤掉对象。 This gives a better explanation of a slug field.

get_object returns 一个对象(你的模型的一个实例),而 get_queryset returns 一个 QuerySet 对象映射到一组可能的模型的多个实例.对于 DetailView(或者实际上继承自 SingleObjectMixin 的任何 class,get_queryset 的目的是 restrict the set of objects from which you'll try to fetch your instance.

如果您想显示实例的详细信息,您必须以某种方式告诉 Django 如何获取该实例。默认情况下,如错误消息所示,Django 调用 get_object 方法在 URL 中查找 pkslug 参数。在您的第一个示例中,pk 在 URL 中,Django 设法自动获取您的实例,因此一切正常。在您的第二个示例中,您覆盖了 get_object 方法并手动使用作为参数传递的 id 来获取对象,这也有效。但是,在第三个示例中,您没有提供 get_object 方法,因此 Django 执行了默认方法。 SingleObjectMixin 的默认 get_object 方法没有找到 pk 或 slug,因此它失败了。

有多种修复方法:

1。在 URL

中使用 pk

最简单的方法是直接使用您在第一个示例中提供的代码。我不知道你为什么不满意,这完全没问题。如果您不满意,请更详细地解释原因。

2。覆盖 get_object

这是您提供的第二个解决方案。这是大材小用,因为如果您使用正确的选项正确配置了视图(正如您将在以下替代方案中看到的那样),Django 会负责为您获取对象。

3。提供 pk_url_kwarg 选项

如果出于某种原因你真的想在 URL 中使用 id,你可以通过指定 pk_url_kwarg 选项在你的视图中指出:

class todoDetailView(DetailView):
    model = models.todo
    pk_url_kwarg = 'id'

4。提供 slug_fieldslug_url_kwarg 选项 [不要这样做]

这是一个糟糕的解决方案,因为您实际上使用的不是 slug,而是 id,但理论上它应该可以工作。您基本上 "fool" Django 会像使用 slug 一样使用 id 字段。我之所以提到它,是因为您在问题中明确询问了这些选项。

class todoDetailView(DetailView):
    model = models.todo
    slug_field = 'id'
    slug_url_kwarg = 'id'

关于您的 get_queryset 方法:在您的示例中,它甚至没有被执行,但无论如何它都被破坏了,因为它 returns 是一个单独的对象而不是一个查询集( objects.get 就是这么做的)。我的猜测是您可能根本不需要自定义 get_queryset 方法。这将很有用,例如,如果您有一个复杂的权限系统,其中不同的用户只能访问 todo 对象的不同子集,我认为这不是您的情况。目前,如果您提供此 get_queryset 方法,即使其他所有配置都正确,您也会收到错误消息。可能是一个 AttributeError 说 queryset 对象没有属性 filter(因为它实际上是一个 todo 对象而不是一个 QuerySet 对象 as Django expects)。

DetailView 的默认 get_object 尝试使用 pkslug 从 URL 获取对象。最简单的方法是在 URL 模式中使用 (?P<pk>[\d]+)

当您覆盖 get_object 时,您将替换此默认行为,因此您不会收到任何错误。

当您覆盖 get_queryset 时,Django 首先运行您的 get_queryset 方法并获取查询集。然后它尝试使用 pkslug 从该查询集中获取对象,并且您收到错误,因为您没有使用它们中的任何一个。

slug_field and slug_url_kwarg 参数都在文档中定义。 slug_fields 是模型中用于获取项目的字段名称,slug_url_kwarg 是 URL 模式中的参数名称。在您的情况下,您正在使用主键 (pk/id) 获取对象,因此您不应使用这些选项中的任何一个。

对于带有 (?P<id>[\d]+) 的 URL 模式,您可以使用 pk_url_kwarg = 'id'。这将告诉 Django 使用 URL 中的 id 获取对象。但是,将您的第一个 URL 模式与 (?P<pk>[\d]+) 一起使用要简单得多,然后您不必覆盖上面的任何 methods/attributes。

get_object() 主要与采用 pk 或 id

的通用视图一起使用

赞:DetailViewUpdateViewDeleteView

其中 get_queryset() 与我们期望更多对象的 ListView 一起使用

此外,get_object()self.get_object() 使用 pk 作为默认查找字段或可以使用 slug 字段

稍微看一下 get_object()

    if queryset is None:
        queryset = self.get_queryset()

    # Next, try looking up by primary key.
    pk = self.kwargs.get(self.pk_url_kwarg)
    slug = self.kwargs.get(self.slug_url_kwarg)
    if pk is not None:
        queryset = queryset.filter(pk=pk)

    # Next, try looking up by slug.
    if slug is not None and (pk is None or self.query_pk_and_slug):
        slug_field = self.get_slug_field()
        queryset = queryset.filter(**{slug_field: slug})