2016-01-28
Modern Django makes me repeat myself in the name of something
One of the things that basically all web frameworks do is URL routing, where they let you specify how various different URL patterns are handled by various different functions, classes, or whatever. Once you have URL routing, you inevitably wind up wanting reverse URL routing: given a handler function or some abstract name for it (and perhaps some parameters), the framework will generate the actual URL that refers to it. This avoids forcing you to hard-code URLs into both code (for eg HTTP redirections) and templates (for links and so on), which is bad (and annoying) for all sorts of reasons. As a good framework, Django of course has both powerful URL routing and powerful reverse URL generation.
Our Django web app is now about five years old and has not been substantially revised since it was initially written for Django 1.2. Back in Django 1.2, you set up URL routing and reversed routing something like this:
urlpatterns = patterns('', (r'^request/$', 'accounts.views.makerequest'), [...]
And in templates, you got reverse routing as:
<a href="{% url "accounts.views.makerequest" %}"> ... </a>
Here accounts.views.makerequest
is the actual function that handles
this particular view. This is close to the minimum amount of
information that you have to give the framework, since the framework
has to know the URL pattern and what function (or class or etc)
handles it.
Starting in Django 1.8 or so, Django changed its mind about how URL reversing should work. The modern Django approach is to require that all of your URL patterns be specifically named, with no default. This means that you now write something like this:
urlpatterns = [ url(r'^request/$', accounts.views.makerequest, name="makerequest"), [...]
And in templates and so on you now use the explicit name, possibly with various levels of namespaces.
Now, Django has a few decent reasons for wanting support for explicit reverse names on URL patterns; you can have different URL patterns map to the same handler function, for example, and you may care about which one your template reverses to. But in the process of supporting this it has thrown the baby out with the bathwater, because it has made there be no default value for the name. If you want to be able to do reverse URL mapping for a pattern, you must explicitly name it, coming up with a name and adding an extra parameter.
Our Django web app has 38 different URL patterns, and I believe
all of them get reversed at some point or another. All of them have
unique handler functions, which means that all of them have unique
names that are perfectly good. But modern Django has no defaults,
so now Django wants me to add a completely repetitive name=
parameter to every one of them. In short, modern Django wants me
to repeat myself.
(It also wants me to revise all of the templates and other code to use the new names. Thanks, Django.)
As you may guess, I am not too happy about this. I am in fact considering ugly hacks simply because I am that irritated.
(The obvious ugly hack is to make a frontend function for url()
that generates the name by looking up the __name__
of the handler
function.)
PS: Django 1.8 technically still supports the old approach, but it's now officially deprecated and will be removed in Django 1.10.