Django's ViewDoesNotExist Heisenbug

To the untrained eye, you might think that you can put any old string in as the second element of one of your Django URL dispatcher patterns in urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,
    (r’^$’, ‘myapp.views.webroot’),
    (r’^a_page/$’, ‘myapp.views.another_page’),
    (r’^nothing_here_dude/$’, ‘myapp.views.this_view_does_not_exist’),
)

You have a fairly basic Django page which works; you add a new tuple to the URL patterns and refresh the page: it works OK, so if everything runs through the URL dispatcher fusebox, then it must be all right, yeah?

Unfortunately, nonexistent view methods seem to break pages that do a reverse lookup in the URL layer using the {% url %} template tag. You might not use this, but the standard Django admin interface does, several times, on its first page after login e.g:

<a href=”{% url django.contrib.admin.views.doc.doc_index %}”>{% trans ‘Documentation’ %}</a>

The above code generates a URL by comparing the view method to the evaluated second arguments of each URL pattern, and evaluation fails at the nonexistent method, giving the error:

ViewDoesNotExist at /admin/
Tried options in module myapp.views. Error was: ‘module’ object has no attribute ‘this_view_does_not_exist’

Sometimes this error isn’t thrown, so perhaps the underlying code searches a dict of URL patterns in no particular order: that would mean that every now and again it returns the matching view without ever getting to the nonexistent method.

The fix is to comment out the URL. But what if you’ve got a big codebase, and there are several people working on it, and sometimes nonexistent view methods just get committed? Testing can help here, specifically interface testing using the fake Client “browser”. That’s not easy when it comes to the out-of-box admin interface, though, because its login procedure seems to require both cookies and a hidden, hashed field to prevent automated logins.

This sounded like it was going somewhere when I started writing it; now, though, I think it’s just handy to publish it and let it have the Googlejuice, as I couldn’t find any references online to other people spotting this solution to a reasonably common problem that just—possibly because another view somewhere else was finally fixed—suddenly vanishes.

Comments

Thanks for the tip. This was driving me crazy until I cam across your post. Truly helpful, should be mentioned in the official tutorial on the Django page.

Thanks for the kind words. I don't know how much goes on the official tutorials, whether this is a bit too specialist. It's good to have independent confirmation, though, as that would suggest that it isn't.