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 then 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
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- Custom managers for Django ForeignKeys
- I’ve got one really annoying model for keywords. There’s one category of keywords that, by default, should not show up when used as a ForeignKey for most models. Key word: most.
- Fixing Django 1.2.4’s SuspiciousOperation on filtering
- When you get the message “Filtering by keyword not allowed” in Django 1.2.4, here’s one way to fix it.
- Reusing Django’s filter_horizontal
- Just as with pop-ups, it’s possible to use the built-in JavaScript for filtering multiple-selection popups on custom forms.
- Django formsets and date/time fields
- Date/Time fields in Django formsets appear to have incompatible default values, resulting in forms using them always looking as though they’ve got a new entry when they don’t.
- 25 more pages with the topic Django, and other related pages
More Python
- Parsing JSKit/Echo XML comments files
- While I’m not a big fan of remote comment systems for privacy reasons, I was willing to use JSKit as a temporary solution because they provide an easy XML dump of posted comments. This weekend, I finally moved my main blog to custom comments; here’s how I parsed JSKit’s XML file.
- Put a relative clock on your Desktop with GeekTool
- There are a lot of desktop clocks that show the absolute time. But sometimes you just want to know if the time is today, or yesterday, or two days ago. Here’s how to do it with Python and GeekTool.
- Multiple tables on the same command
- The way the “random” script currently stands, it does one table at a time. Often, however, you have more than one table you know you’re going to need. Why not use one command to rule them all?
- Easier random tables
- Rather than having to type --table and --count, why not just type the table name and an optional count number?
- Programming for Gamers: Choosing a random item
- If you can understand a roleplaying game’s rules, you can understand programming. Programming is a lot easier.
- 24 more pages with the topic Python, and other related pages
