How useful is Django?
I was recently asked “how do you find Django?” after mentioning that I’ve been using it to maintain my web pages.
The answer is, “somewhat conflicted”, but on the balance very useful. It is very, very easy to go from an idea to a working database/application combination. Just this weekend I was adding a couple of new albums to my albums database and I decided it was time to convert it to Django/SQLite. The Django setup was a snap. The import from FileMaker (using appscript) was a snap. Getting the multiple columned PDF that I take to record shops was fine. I spent more time than I should have trying to figure out how to make the third page rotate 180 degrees before giving up, but it went pretty well.
One of the reasons I (use to) use FileMaker is because it’s easy to set up layouts, but with CSS editors such as CSSEdit, creating nice layouts in HTML is pretty easy, too. With CSS3 support coming on line, even multiple columns broken up across a page will be possible, removing the necessity for tools such as reportlab.
There appear from the outside to be two poles in the Django project: easy to use and quick to set up on one end; and an attachment to some conventions that tend to break outside of Django on the other end. It’s the former pole that appeals to me. The prime examples of the latter are probably autodating changes and discouraging NULL in string columns but not other columns.
The current stable version of Django makes it easy to keep track of when a record changes; the current unstable version does not, and the likely recommendation appears to be for people to override each model’s “save” method. Having to override methods for basic functionality takes quite a bit away from Django’s ease of use. It may be fixed before the next stable version, though.
The NULL issue is a bit more complex. The Django model reference describes it this way:
Avoid using null on string-based fields such as CharField and TextField unless you have an excellent reason. If a string-based field has null=True, that means it has two possible values for “no data”: NULL, and the empty string. In most cases, it’s redundant to have two possible values for “no data;” Django convention is to use the empty string, not NULL.
The problem is that in all SQL databases, the empty string is never “no data”. Only NULL means “no data”, which means that Django will die horribly if more than one record has no data in a unique string column. For all practical purposes, without overriding a model’s “save” method string fields can’t support SQL’s option of allowing no data in a unique column. For that matter, even in Django the other field types do use NULL values to mean “no data”.
I can’t see any place where the combination of null=True, blank=True, and unique=True will ever make sense using the empty string to mean “no data”.
In general, though, Django is extremely flexible. I’ve integrated Mako into the Django template language, so that I can use Django’s easy template system when I want it, and then fall right into full-on Python when I need a full programming language.
Here’s a simple example of what makes Django so cool even for people with a minimal familiarity with the command line, and the problems I have with it:
[toggle code]
-
class Post(models.Model):
- title = models.CharField('Title', maxlength=120)
- description = models.TextField('Description', maxlength=500)
- slug = models.SlugField('Unique Reference', unique=True, maxlength=25, prepopulate_from=("title",))
- content = models.TextField('Content', blank=True)
- parent = models.ForeignKey('self', blank=True, null=True)
- filename = models.CharField('File Name', maxlength=30, blank=True, null=True)
- offSiteURL = models.CharField('Off Site URL', maxlength=160, blank=True, null=True, unique=True)
- added = models.DateTimeField('Date Created', auto_now_add=True)
- edited = models.DateTimeField('Last Modified', auto_now=True)
- publishon = models.DateTimeField('Publish on date', editable=False, blank=True)
-
class Admin:
- list_display = ('title', 'edited', 'publishon')
- list_filter = ['edited', 'added']
- date_hierarchy = 'edited'
- search_fields = ['title','slug','offSiteURL', 'filename']
- pass
-
class Meta:
- unique_together = (('parent', 'filename'),)
- ordering = ['title']
This is all I need to create a model of a post. If I then type “python manage.py syncdb” it will create the MySQL or SQLite tables for me, set up any cross-reference tables it needs, set up an administrative interface for adding and managing posts… it’s awesome, except that it will probably fail after two or three posts, depending on the ratio of off-site to on-site postings. What’s going to happen is that if there are more than one off-site postings for a given “parent” post, the “uniqueness” requirement that there be no two filenames with the same parent will fail in the SQL database. The SQL database will complain, Django won’t know what to do, and it will throw up an error every time I try to edit or add a page.
Similarly, as soon as more than two items in the database don’t have an off-site URL, the same thing will happen: Django’s notion of “no data” will conflict with SQL’s, and the whole thing comes crashing down.
“No data” has a very specific meaning in SQL; it means “we don’t know what this is, it may not even exist”. Django’s meaning of “no data” appears to be something else.
More good stuff
The tables that Django creates are easy to recognize for what they are, making it easy (if you’re so inclined) to write separate scripts to access the data without using Django. However, the Django API is so useful I almost never do; my command-line scripts use the Django API to mass-change data.
Adding new features is easy, as long as the feature can be conceived of as either a collection of SQL tables, a method on a table’s model, or as a template tag for insertion in a template file.
More bad stuff
Other than minor concerns with the direction of Django, the only other big problem I have with Django is the general lack of documentation for Python projects. This is not specifically a Django problem. Django itself is well-documented, comparatively (as is Mako), but other Python modules (Paramiko, Reportlab) can be a real pain to figure out, and the Python documentation itself tends to suck. It has few examples and what examples it does have tend to be a restatement of the syntax definition—often using non-working snippets. Coming from the php.net/manual documentation, Python’s “elusive documentation” is a real roadblock. I still haven’t figured out what encode and decode really do, despite their seemingly obvious names.
On his blog, Russell Beattie wrote “I honestly don’t know how anyone ever understands how to get anything done beyond a basic for loop, as every time I try to look something specific up, it’s either NOT CLEAR or NOT THERE.”
I agree with Russell on this. I stumbled into Python due to, first, Mailman, and then later Django. I only started using Django because I’d heard about how cool Ruby on Rails was, but when I downloaded Rails the development server kept crashing after every connection. So I Googled “PHP Rails” and then, on a lark, “Python Rails”. That brought me to Django, which worked. Django’s documentation also worked. If not for Django I would never have started using Python for serious projects. I still sometimes question whether I should be using it—usually when I’m knee-deep in a Python library with no working documentation.
Good documentation is especially important in a project like Django, which is still undergoing major changes. The general advice in areas such as newforms where Django isn’t as well documented as it should be—look at the source code—will result in finding spurious solutions, and hard-coding side-effects into your code that were never meant to be used publicly.
That said, there’s nothing else I’d rather be using to manage my site and other data right now. If it turns out that the next version of Django doesn’t work for me, I’ll happily continue using the current 0.96. It’s pretty damn cool.
- Django model reference
- “A model is the single, definitive source of data about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table.”
- appscript
- Appscript is a high-level, user-friendly MacPython to Apple event bridge that allows you to control scriptable Mac OS X applications using ordinary Python scripts. Appscript makes MacPython a serious alternative to Apple’s own AppleScript language for automating your Mac.
- Mako
- “Mako is an embedded Python language, which refines the familiar ideas of componentized layout and inheritance to produce one of the most straightforward and flexible models available, while also maintaining close ties to Python calling and scoping semantics.”
- Using libxml in Python
- “Python-XML projects have been notorious for poor documentation, which is one of the considerations that inspires this column. It was especially difficult for me to get a handle on libxml because of its remarkable richness and thus complexity, combined with elusive documentation.”
- Python Library Docs Still Suck
- “I’ve been bitching about this for many years now, but I’m still fascinated at how popular Python is given how insanely user hostile it’s library documentation is. I honestly don’t know how anyone ever understands how to get anything done beyond a basic for loop, as every time I try to look something specific up, it’s either NOT CLEAR or NOT THERE.”
- CSSEdit
- “CSSEdit offers unrivaled real-time styling of absolutely any web page. Even when your dynamic Web App is powered by a complex database or makes use of advanced AJAX, you can style and analyze it without the hassle of uploading or refreshing.”
More Django
- Converting an existing Django model to Django-MPTT
- Using a SQL database to mimic a filesystem will, eventually, create bottlenecks when it comes to traversing the filesystem. One solution is modified preordered tree traversal, which saves the tree structure in an easily-used manner inside the model.
- Two search bookmarklets for Django
- Bookmarklets—JavaScript code in a bookmark—can make working with big Django databases much easier.
- Fixing Django’s feed generator without hacking Django
- It looks like it’s going to be a while before the RSS feed generator in Django is going to get fixed, so I looked into subclassing as a way of getting a working guid in my Django RSS feeds.
- ModelForms and FormViews
- This is just a notice because when I did a search, nothing came up. Don’t use ModelForm with FormView, use UpdateView instead.
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- 29 more pages with the topic Django, and other related pages
More Python
- Quick-and-dirty old-school island script
- Here’s a Python-based island generator using the tables from the Judges Guild Island Book 1.
- Astounding Scripts on Monterey
- Monterey removes Python 2, which means that you’ll need to replace it if you’re still using any Python 2 scripts; there’s also a minor change with Layer Windows and GraphicConverter.
- Goodreads: What books did I read last week and last month?
- I occasionally want to look in Goodreads for what I read last month or last week, and that currently means sorting by date read and counting down to the beginning and end of the period in question. This Python script will do that search on an exported Goodreads csv file.
- Test classes and objects in python
- One of the advantages of object-oriented programming is that objects can masquerade as each other.
- Timeout class with retry in Python
- In Paramiko’s ssh client, timeouts don’t seem to work; a signal can handle this—and then can also perform a retry.
- 30 more pages with the topic Python, and other related pages