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 Daniel Greenfeld, 13 years ago

Component: DocumentationGeneric views
Summary: ListView docs need to be more explicit in regards to model managersListView need to be more explicit in regards to model managers

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.

comment:2 by Daniel Greenfeld, 13 years ago

Type: UncategorizedBug

comment:3 by Daniel Greenfeld, 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 Preston Holmes, 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 Daniel Greenfeld, 13 years ago

Component: Generic viewsDocumentation
Type: BugUncategorized

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 Preston Holmes, 13 years ago

Summary: ListView need to be more explicit in regards to model managerscalling as_view of CBV in URLConf needs better documentation and examples
Triage Stage: UnreviewedAccepted

hoping to address this in #16807

comment:7 by Aymeric Augustin, 11 years ago

Type: UncategorizedNew feature

comment:8 by Bibhas C Debnath, 9 years ago

Cc: me@… added

#16807 was fixed long time back. Is this ticket still relevant?

comment:9 by Tim Graham, 9 years ago

I don't think that ticket ended up adding any clarification regarding the issues presented here.

comment:10 by Baptiste Mispelon, 8 years ago

Resolution: fixed
Status: newclosed

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

Note: See TracTickets for help on using tickets.
Back to Top