Django detailview的get_queryset和get_object
Django detailview的get_queryset和get_object
我正在使用Django的detailview。最初,我使用了URL模式
url(r'^todo/details/(?P[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),
我的视图是
class todoDetailView(DetailView): model = models.todo
它工作得很好。
在第二种情况下,我的URL是
url(r'^todo/details/(?P[\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获取详细信息,我在StackOverflow上读到
可以使用此代码
slug_field = 'param_name' slug_url_kwarg = 'param_name'
链接- Generic detail view ProfileView must be called with either an object pk or a slug
有人可以解释一下get_object()和get_queryset()(如果可能,还有get_slug_field())的实际工作方式吗?
以及slug_field和slug_url_kwarg这些术语
提前致谢
DetailView
的默认get_object
会尝试通过从URL中获取pk
或slug
来获取对象。你最简单的做法是在URL模式中使用(?P
。
当你重写get_object
时,你替换了默认的行为,因此你不会遇到任何错误。
当你重写get_queryset
时,Django会首先运行你的get_queryset
方法来获取查询集。然后尝试从该查询集中使用pk
或slug
获取对象,由于你没有使用其中任何一个,所以你会遇到错误。
slug_field
和slug_url_kwarg
参数在文档中都有定义。 slug_fields
是模型中用于获取项目的字段名称,slug_url_kwarg
是URL模式中参数的名称。 在你的情况下,你使用主键(pk
/id
)获取对象,所以你不应该使用这些选项中的任何一个。
对于你的URL模式,使用(?P
,你可以使用pk_url_kwarg = 'id'
。这将告诉Django从URL中使用id
获取对象。不过,更简单的方法是使用第一个URL模式(?P
,这样你就不必重写以上任何方法/属性。
get_object
返回一个对象(一个你的模型的实例),而get_queryset
返回一个QuerySet对象,映射到你的模型的一组潜在的多个实例。对于DetailView
(或者实际上任何继承自SingleObjectMixin
的类),get_queryset
的目的是限制你尝试获取实例的对象集合。
如果你想显示一个实例的细节,你必须以某种方式告诉 Django 如何获取那个实例。默认情况下,正如错误消息所示,Django 调用get_object
方法,在 URL 中查找pk
参数或slug
参数。在你的第一个示例中,在 URL 中有pk
,Django 可以自动获取你的实例,所以一切都正常。在你的第二个示例中,你覆盖了get_object
方法,并手动使用传递的id
作为参数来获取对象,这也起作用了。然而,在第三个示例中,你没有提供get_object
方法,所以 Django 执行了默认的方法。SingleObjectMixin 的默认get_object
方法没有找到
有多种方法可以修复它:
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_field
和slug_url_kwarg
选项[不要这样做]
这是一种可怕的解决方法,因为你并没有真正使用一个 slug,而是一个 id,不过理论上应该可以工作。你将基本上“欺骗”Django,使其将id
字段像它是一个 slug 一样使用。我之所以提到它,是因为你在问题中明确询问了这些选项。
class todoDetailView(DetailView): model = models.todo slug_field = 'id' slug_url_kwarg = 'id'
关于你的get_queryset
方法:在你的示例中,它甚至没有被执行,但无论如何,它都是错误的,因为它返回一个单独的对象,而不是一个查询集(那是objects.get
做的)。我猜测,你可能根本不需要自定义get_queryset
方法。例如,如果你有一个复杂的权限系统,在该系统中,不同的用户只能访问不同的todo
对象的子集,我假设这不是你的情况。如果你提供了这个get_queryset
方法,即使其他一切都正常,你也会收到一个错误。很可能是一个AttributeError,说queryset
对象没有filter
属性(因为它实际上是一个todo
对象,而不是一个 QuerySet 对象,正如 Django 所期望的一样)。