I am using Django detailview. initially, I used the URL pattern
url(r'^todo/details/(?P<pk>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),
my view is
class todoDetailView(DetailView):
model = models.todo
It worked fine.
In the second case, my URL is
url(r'^todo/details/(?P<id>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),
this time, I modified my view to
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'])
It worked fine, I modified the second case to
class todoDetailView(DetailView):
model = models.todo
# context_object_name = 'todo_detail'
def get_queryset(self):
return models.todo.objects.get(id=self.kwargs['id'])
then I get an error,
Generic detail view todoDetailView must be called with either an object pk or a slug.
I know that there is no proper slug or pk provided. So, initially I added get_object() (it worked) but get_queryset() is not working. What is the difference in their working ??
And also if a user is getting details only based on the slug, I read on StackOverflow that
this can be used
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
Can anyone explain me actual working of get_object() and get_queryset() (also get_slug_field() if possible)
Along with the terms slug_field
and slug_url_kwarg
Thanks in advance
get_object
returns an object (an instance of your model), while get_queryset
returns a QuerySet object mapping to a set of potentially multiple instances of your model. In the case of the DetailView
(or in fact any class that inherits from the SingleObjectMixin
, the purpose of the get_queryset
is to restrict the set of objects from which you'll try to fetch your instance.
If you want to show details of an instance, you have to somehow tell Django how to fetch that instance. By default, as the error message indicates, Django calls the get_object
method which looks for a pk
or slug
parameter in the URL. In your first example, where you had pk
in the URL, Django managed to fetch your instance automatically, so everything worked fine. In your second example, you overrode the get_object
method and manually used the id
passed as parameter to fetch the object, which also worked. In the third example, however, you didn't provide a get_object
method, so Django executed the default one. SingleObjectMixin's default get_object
method didn't find either a pk or a slug, so it failed.
There are multiple ways to fix it:
pk
in the URLThe simplest one is to simply use the code you provided in your first example. I don't know why you were unsatisfied with that, it is perfectly fine. If you're not happy, please explain why in more detail.
get_object
This is the second solution you provided. It is overkill because if you properly configured your view with the correct options (as you will see in the following alternatives), Django would take care of fetching the object for you.
pk_url_kwarg
optionIf you really want to use id
in the URL for some reason, you can indicate that in your view by specifying the pk_url_kwarg
option:
class todoDetailView(DetailView):
model = models.todo
pk_url_kwarg = 'id'
slug_field
and slug_url_kwarg
options [DON'T DO THIS]This is a terrible solution because you are not really using a slug, but an id, but it should in theory work. You will basically "fool" Django into using the id
field as if it was a slug. I am only mentioning it because you explicitly asked about these options in your question.
class todoDetailView(DetailView):
model = models.todo
slug_field = 'id'
slug_url_kwarg = 'id'
Regarding your get_queryset
method: in your example, it doesn't even get to be executed, but in any case it is broken because it returns an individual object instead of a queryset (that's what objects.get
does). My guess is you probably don't need a custom get_queryset
method at all. This would be useful for example if you had a complex permission system in which different users can only access a different subset of todo
objects, which I assume is not your case. Currently, if you provide this get_queryset
method, even if everything else is configured properly, you will get an error. Probably an AttributeError saying that the queryset
object has no attribute filter
(because it will actually be a todo
object and not a QuerySet object as Django expects).
The default get_object
for DetailView
tries to fetch the object using pk
or slug
from the URL. The simplest thing for you to do is to use (?P<pk>[\d]+)
in the URL pattern.
When you override get_object
, you are replacing this default behaviour, so you don't get any errors.
When you override get_queryset
, Django first runs your get_queryset
method an fetches the queryset. It then tries to fetch the object from that queryset using pk
or slug
, and you get an error because you are not using either of them.
The slug_field
and slug_url_kwarg
parameters are both defined in the docs. The slug_fields
is the name of the field in the model used to fetch the item, and slug_url_kwarg
is the name of the parameter in the URL pattern. In your case, you are fetching the object using the primary key (pk
/id
), so you shouldn't use either of these options.
For your URL pattern with (?P<id>[\d]+)
, you could use pk_url_kwarg = 'id'
. That would tell Django to fetch the object using id
from the URL. However, it's much simpler to use your first URL pattern with (?P<pk>[\d]+)
, then you don't have to override any of the methods/attributes above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With