Django impressions by a Plonista
Lately I’ve been working with Django, and I’m going to share the impressions I have of Django, from a longtime Zope/Plone users view point. Of course Django is a good web framework. If it wasn’t, it wouldn’t be as wildly popular as it is. So needless to say, the impression is good, overall. I like Django, and am now looking for more Django work!
I have been looking at Django earlier, but that was years ago, and things have changed. So to a large extent this is a newbie-look at Django. It may or may not be part 1 in a series, we’ll see.
Convenience vs ideology
Zope/Plone uses traversal as it’s URL scheme. That is the correct scheme if you are making hierarchical content, such as a content management system. But if that’s not what you are doing, it’s not quite as practical. In general you end up having a flat structure of objects and views on them, and a bunch of views on the root/site object. That works well, but not as well as having the so called “URL Dispatch”.
URL dispatch is about defining a list of URL’s and which view that should be used for each URL. This often makes more sense for a web application that doesn’t have hierarchical content. The way Django implements URL traversal and reverse lookups makes this really easy, and it is especially simple to reorganize the URL’s of your application. I have had some problems with getting the reverse lookup to work correctly with the parameters needed, etc, but that was nothing too bad.
It’s also interesting to see the differences between Django and another very good web framework: Pyramid. Django’s API and way of handling things is geared towards convenience, while Pyramid is geared towards something that I can only describe as “Pythonicism”. Django defines paths in a tuple that is magically parsed somewhere by Django. Pyramid makes you call a function to add paths. It’s more typing, but also less magical. Django has a
reverse() method to create paths from a path name and parameters, Pyramid uses a method on the request object, which certainly makes sense and enables you to make full URLs. It’s a matter of opinion what you prefer, personally I can’t make up my mind.
Pyramid of course has the benefit that it can do both URL dispatch and traversal, so you don’t have to choose, but that’s besides the point here.
I think class-based views is the best thing to ever happen to Django, and that people complaining about it should stop. OK, so I’m coming from the Zope world, where we have used class-based views for years now, and no-one in their right mind would do anything else. And Zope is just full of multiple inheritance, so I have no problem with that either, and let me tell you, the multiple inheritance of Zope is not as clean or easily understood as the one of Django’s class-based views.
Class-based views are not only about moving Django onto the same Model-View-Template pattern that everyone else uses, and has been shown to be a popular, flexible solution, it also makes Django easier to use for people coming from other modern web frameworks, and it enables reusing of code in a better way. The old ways with functions wasn’t a good idea. Full kudos to the Django developers for deprecating function-based generic views.
Do you really need to “understand” multiple-inheritance to use class-based views? Well, yes and no. You need to know that your mixin need to come first in the list of classes you inherit from. And you need to know that you should use
super(). But that isn’t really understanding multiple inheritance, it’s just simple rules to follow. It certainly does help if you are to understand the existing class-hierarchy by reading the code. It could on the other hand be claimed that you shouldn’t need to read the code, and that the documentation should be enough. It isn’t though, which brings me up to the first complaint.
The Django tutorial was good, but it stops short. It doesn’t really explain how to use the generic views for editing that is included. This turns out to be a big hole, because they aren’t really explained in full anywhere else either. Quite often you need to piece together the information from several places. The documentation on the generic class-based views only explains that specific class, and only lists the methods introduced in that class. To find the documentation on the other methods, you have to go to the parent classes. It often becomes easier to just read the code, especially if you have a good IDE that will have class-browser that show you all the methods the class have, for example. But although a good IDE in this case works as a replacement for documentation, it would have been better if it didn’t need to.
I did find after some searching a page that explain all or most of how to do generic edit views, except it doesn’t have examples of templates. This ended up being quite a big time waster. I found some various examples of templates outside of the Django docs, which seems the wrong place to do it, and those templates where not always good. Also I made a subtle error. I used
action="."on the forms. That turns out to work on the create form, but not on the delete form. Why? No clue. Changing it to
action="", or since I use HTML5, simply removing
action="" altogether, made it work.
There is here an opportunity to improve the documentation. Not much extra is needed. Another chapter on the generic edit views in the tutorial, and overviews of the classes and all their methods, so you don’t have to jump around in many places. And lastly and perhaps most importantly: Include examples of templates. It may be obvious to the Django developers that you don’t have to post anything except the
csrf_token to confirm a delete, but that’s not obvious to anyone else. With code examples it becomes immediately obvious.
The silence of the
One problem I had many times is that I wouldn’t understand why something didn’t show up in my templates. No errors, no nothing, even though I’m running in debug mode, and yes,
TEMPLATE_DEBUG is also True. Yet, nothing.
This turns out to be intentional, and a decision taken early in Django’s design. That decision was incorrect. I understand that it is now to late to change it, bit nonetheless that was the wrong decision. What could be done now is to change it so that it will fail when
TEMPLATE_DEBUG is true, and I can see that it was discussed in depth at least in 2008. And this wasn’t done then either. Which again, and let me mince no words about that, was the wrong decision.
A workaround for this exists, and it’s called
TEMPLATE_STRING_IF_INVALID. You should in all your projects add this to settings.py:
TEMPLATE_STRING_IF_INVALID = 'INVALID EXPRESSION: %s' if DEBUG else ' '
So why is this not the default in Django already? Well, the documentation offers a hint:
Many templates, including those in the Admin site, rely upon the silence of the template system when a non-existent variable is encountered.
Ugh. Don’t do that! Seriously.
There is one thing haven’t made up my mind about, and that’s the executable settings. It is certainly convenient. You can, like above set settings dependent on other settings easily. It doesn’t require you to learn a configuration syntax. You’ll have no bugs in the configuration parsers. So it makes sense. And Daniel J Bernstein says that parsing is bad because it causes bugs. On the other hand, he says to reduce code, so… I can’t come up with any arguments about it, but it feels icky. Arguments one way or another are welcome.
There are some flaws with Django, but they are compensated by Django’s general leaning towards convenience. This overall makes Django a good web framework to start with, and a nice rapid development environment for anyone that wants to do web.
If you have decided to use Django, the decision was definitely not wrong.