Technology, programming, python, work and interesting things

Django generic views cache-like behavior

In certain cases the Django generic views will behave like they are caching data. They are doing that for the queryset argument and not for extra_context which is known to be cached. This will happen if you send to the generic view a queryset filtered with a callable.

Let's take as an example a blogging application. In blog.models we have:

class PublishedManager(Manager): 
def get_query_set(self):
queryset = super(PublishedManager, self).get_query_set()
return queryset.filter(pub_date__lte=datetime.now)

class Entry(models.Model):
...
pub_date = models.DateTimeField()
published = PublishedManager()
...

In blog.urls:

info_dict = {'queryset': Entry.published.all()} 

entry_list = url( regex = '^$',
view = 'django.views.generic.list_detail.object_list',
kwargs = dict(info_dict, paginate_by=10),
name = 'entry-list' )

urlpatterns = patterns('', entry_list)

Internally, the generic view will _clone() the queryset sent in the info_dict, in order to get fresh data from the database. Unfortunately, at the time of the cloning, the queryset already has the filter applied with the datetime.now callable already invoked and the datetime value cached in the where clause. The generic view will always return the entries that have the pub_date less then or equal to the time when blog.urls module was loaded.

The work-around for this issue is to stop passing the queryset to the generic view through the info_dict dictionary. We can do that by creating our own view:

def entry_list(request, page=0): 
return django.views.generic.list_detail.object_list( request,
queryset = Entry.published.all(),
paginate_by = 10,
page = page )

This way every time the view is called a new queryset will be created. This new queryset will always have in the where clause the current datetime.


Comments are closed.

Copyright © 2008-2011 Mihail Ovidiu Pascut