views:

234

answers:

3

I have a custom model manager that looks like this:

class MyManager(models.Manager)
    def get_query_set(self):
        '''Only get items that are 'approved' and have a `pub_date` that is in
        the past.  Ignore the rest.'''
        queryset = super(MyManager, self).get_query_set()
        queryset = queryset.filter(status__in=('a',))
        return queryset.filter(pub_date__lte=datetime.utcnow())

And this works well enough; however, I have a problem using Django's generic.list_detail views object_detail and object_list: the queryset seems to be only loading once and, because of this, it isn't fetching the items it should be because, I assume, the utcnow() time has been called only once (when it first loaded).

I assume this is intentional and meant as a performance boost - however, it means that video's show up elsewhere on the site (in places I am not in a object_detail view) before they are available in an object_detail view (see urls.py below). This is leading to 404s ...

Any ideas ? Or do I have to write my own custom views to avoid this ?

Thanks!

urls.py

url(r'^video/(?P<object_id>\d+)$', 
    list_detail.object_detail,
    {   'queryset': Video.objects.all(), },
    name='video_detail',
),
+1  A: 

Try correcting urls.py to:

url(r'^video/(?P<object_id>\d+)$', 
    list_detail.object_detail,
    {   'queryset': Video.objects.all, }, # here's the difference
    name='video_detail',
)

Edit:

If this fail, try apply similar technique(passing callable instead of calling it) to filter():

return queryset.filter(pub_date__lte=datetime.utcnow)
gorsky
I had tried that but get a `'function' object has no attribute 'model'` error - because the `list_detail` view clone's it ...
thornomad
QuerySets are lazy (http://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy), it shouldn't be a problem to call `all` or `filter` in an URL pattern. This is even explained somewhere in the docs, but I can't find it right now.
piquadrat
Check edit, hope this helps.
gorsky
+1  A: 

It is not a problem of cache: as you do it now, the queryset definition is evaluated once, while parsing urls, and then, it is never evaluated again.

Solution is actually pretty simple and described in the online documentation: Complex filtering with wrapper functions: just create a small custom view, that will simply call the generic view.
I am actually using a similar solution quite a lot and I feel it quite comfortable.

By the way, a small side note, for this case I would suggest not using a custom manager, and go back instead on a normal filtering.

Roberto Liffredo
Wrapping generic views is a fine technique, but not needed in this case. All that's needed here is to remove the () from Video.objects.all.
Carl Meyer
A: 

I have an almost identical model Manager to thornomad, and the same problem with generic views.

I have to point out that neither of the suggestions above work:

  • doing Video.objects.all without parentheses gives an error
  • doing queryset.filter(pub_date__lte=datetime.utcnow), again without the parentheses, does not give an error but does not fix the problem

I have also tried another way, which is to use a lambda to return the queryset, eg:

qs = lambda *x: Video.objects.all()
url(r'^video/(?P<object_id>\d+)$', 
    list_detail.object_detail,
    {   'queryset': qs(), },
    name='video_detail',
),

...it didn't work either and I can see now I must have been desperate to think it would :)

lazy_qs = lambda *x: lazy(Post.live_objects.all, QuerySet)

blog_posts = {
    'queryset': lazy_qs(),

...doesn't work either (gives an error) as utils.functional.lazy doesn't know how to convert the result to a QuerySet properly, as best I can tell.

I think Roberto's answer of wrapping the generic view is the only one that will help.

The django docs should be amended to point out the limitations of the queryset used by generic views (currently the docs have a special note to tell you everything will be okay!)

Anentropic