Opened 13 years ago
Closed 8 years ago
#16970 closed New feature (fixed)
calling as_view of CBV in URLConf needs better documentation and examples
Reported by: | Daniel Greenfeld | Owned by: | nobody |
---|---|---|---|
Component: | Documentation | Version: | 1.3 |
Severity: | Normal | Keywords: | |
Cc: | me@… | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Say I have a ListView that calls on a Thing model. Thing model has it's own Model Manager that adds query method called 'some_things'. In theory I code it thus:
url(regex=r'^$', view=ListView.as_view( queryset=Thing.objects.some_things(), template_name='things/some_things.html'), name='some_things', ),
However, (MultipleObjectMixin.get_queryset) https://code.djangoproject.com/browser/django/trunk/django/views/generic/list.py shows that if a model is not defined in the ListView arguments, that the default manager all method is called. Which means if you want to get the behavior you want you need to do:
url(regex=r'^$', view=ListView.as_view( model=Job, queryset=Thing.objects.some_things(), template_name='things/some_things.html'), name='some_things', ),
See how I added an explicit model call?
Figuring this out meant rooting through Django source code. What I propose is documenting this behavior in the pages on Class Based Views. I would do it myself but it is unclear where I should put said documentation (topic or not?).
Change History (10)
comment:1 by , 13 years ago
Component: | Documentation → Generic views |
---|---|
Summary: | ListView docs need to be more explicit in regards to model managers → ListView need to be more explicit in regards to model managers |
comment:2 by , 13 years ago
Type: | Uncategorized → Bug |
---|
comment:3 by , 13 years ago
I'll be documenting this with a real sample of the bug in the next 24 hours. Just need to scrape the client specific parts from the code and paste it in.
comment:4 by , 13 years ago
I'm assuming Job==Thing in your description.
One thing is that your queryset will be evaluated at URL import time - and that is probably not what you wanted.
Since you want to define a custom queryset for each time the view is visited, what you probably want is:
url(regex=r'^$', view=ListView.as_view( get_queryset=Thing.objects.some_things, template_name='things/some_things.html'), name='some_things', ),
which will result in your custom manager being called each time the view goes to get a list of objects (see the note in the docs "Thread safety with view arguments" in https://docs.djangoproject.com/en/1.3//ref/class-based-views/#django.views.generic.base.View)
Furthermore, the queryset attr is only replace with a call to the default manager if queryset is None, so I'm not sure how you would be seeing the default manager being called in your example, even if you also set the model
comment:5 by , 13 years ago
Component: | Generic views → Documentation |
---|---|
Type: | Bug → Uncategorized |
I think it's pretty clear from @ptone's comment that this is not a bug then, it's a documentation issue. Changing the ticket type accordingly.
comment:6 by , 13 years ago
Summary: | ListView need to be more explicit in regards to model managers → calling as_view of CBV in URLConf needs better documentation and examples |
---|---|
Triage Stage: | Unreviewed → Accepted |
hoping to address this in #16807
comment:7 by , 12 years ago
Type: | Uncategorized → New feature |
---|
comment:8 by , 10 years ago
Cc: | added |
---|
#16807 was fixed long time back. Is this ticket still relevant?
comment:9 by , 10 years ago
I don't think that ticket ended up adding any clarification regarding the issues presented here.
comment:10 by , 8 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
I believe this issue has been fixed. I tried the following:
# models.py from django.db import models class CustomManager(models.Manager): def some_things(self): return self.all()[:3] class Thing(models.Model): objects = CustomManager() # urls.py from django.conf.urls import url from django.views.generic import ListView from .models import Thing urlpatterns = [ url( regex=r'^$', view=ListView.as_view( queryset=Thing.objects.some_things(), template_name='things/some_things.html', ), name='some_things', ), ] # tests.py from django.test import TestCase, override_settings from .models import Thing @override_settings(ROOT_URLCONF='things.urls') class ListViewTests(TestCase): @classmethod def setUpTestData(cls): for _ in range(5): Thing.objects.create() def test_items(self): res = self.client.get('/') self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'things/some_things.html') self.assertEqual(len(res.context['object_list']), 3)
As noted in comment 4, there could be an issue that declaring the queryset
at the module-level could make the object reused between successive calls to the view, but as noted in the documentation [1], the default implementation of get_queryset
prevents that by systematically cloning the queryset object.
I'm therefore closing this.
[1] https://docs.djangoproject.com/en/1.9/ref/class-based-views/mixins-multiple-object/#django.views.generic.list.MultipleObjectMixin.queryset
In retrospect, this is not a documentation issue but a real bug. If a queryset is specified, why is it being overridden? So I call bug.