django snakebite: when things get evaluated
I just got bit by a small oversight on my part that seemed elusive and destructive enough to post here. It’s easier to show than explain, so here’s the code in question:
from django.conf.urls.defaults import *
from django.views.generic import list_detail
from links.models import LinkCategory, Link
urlpatterns = patterns(
”,
(r‘^$’,
list_detail.object_list,
{ ‘queryset’: LinkCategory.objects.all(),
‘template_object_name’: ‘category’,
‘extra_context’: {‘uncategorized’: Link.published_objects.filter(category__isnull=True)}
}, ‘links-linkcategory-list’
),
)
This uses django’s list_detail generic view to list links sorted by category (the template uses the LinkCategory.link_set manager to render each category’s links). To catch uncategorized links I got clever and brought another queryset into the template context that finds all links with an empty ‘category’ field.
This worked just fine in development, when I was trying my project out using Django’s development server. When I ran it on my server under mod_python however, the ‘uncategorized’ list wouldn’t update! I could add new uncategorized links in the admin but they wouldn’t show up in the view until I restarted apache.
It’s probably pretty clear what the problem was, but it wasn’t clear to me at first (and that’s why I’m writing it up). My clever call to Link.objects.filter(category__isnull=True) only gets evaluated once, the first time the URLconf is loaded. The development server is reloading itself pretty regularly (unless you run with the –noreload option), which is why I never saw this behavior in development; but apache only reloads the module when it gets restarted.
My solution was to write a cron job that restarts apache every 15 seconds.
Just kidding!
The clean solution is to get this logic out of my URLconf and put it somewhere more natural and re-usable (like a template tag). But I’m feeling lazy so here’s a lazy solution:
Just enclose the activity in an anonymous function and it gets evaluated anew each time the template is rendered.
If it seems a little unfair that this should work:
But that’s due to some work in the definition of list_detail.object_list (and all the other generic views that come with Django):
# eventually, on line 46 or so…
queryset = queryset._clone()
That queryset._clone() method does just what it says - and now we have a fresh, uncached queryset to be evaluated and rendered into the template.
About this entry
You’re currently reading “ django snakebite: when things get evaluated ,” an entry on jake elliott @ dai5ychain
- Published:
- 9.21.07 / 12am
- Category:
- programming, django
No comments
Jump to comment form | comments rss [?] | trackback uri [?]