Finishing Touches
Using the key concepts laid out so far, you could go forward and refine this primitive blog engine in a number of ways. Let’s step through a few of them to make this project feel just a little more polished.
Template Niceties
Our template is plain to say the least. Because this is a book on Web programming not Web design, we leave the aesthetic touches to you, but template inheritance is another feature of the template system that can make your life easier, especially as your page styles proliferate.
Our simple template is completely self-contained. But what if our site had a blog, a photo archive, and a links page, and we wanted all these to be based on a common base? Experience tells you the wrong way to do this would be to copy and paste your way to three kind-of-identical self-contained templates. The right way in Django is to create a base template, and then extend this template to generate the other, specific templates. In your mysite/blog/templates directory, create a template called base.html containing the following:
<html> <style type="text/css"> body { color: #efd; background: #453; padding: 0 5em; margin: 0 } h1 { padding: 2em 1em; background: #675 } h2 { color: #bf8; border-top: 1px dotted #fff; margin-top: 2em } p { margin: 1em 0 } </style> <body> <h1>mysite.example.com</h1> {% block content %} {% endblock %} </body> </html>
Not exactly valid XHTML Strict, but it’ll do. The detail to notice is the {% block ... %} tag. This defines a named area that subtemplates can change. To make your blog app use this template, change your archive.html template so it references this new base template and its “content” block.
{% extends "base.html" %} {% block content %} {% for post in posts %} <h2>{{ post.title }}</h2> <p>{{ post.timestamp }}</p> <p>{{ post.body }}</p> {% endfor %} {% endblock %}
The {% extends ... %} tag tells Django to look for a template named base.html, and plug the content of any named blocks in this template into the corresponding blocks in that template. You should now see something like Figure 2.10 (hopefully your blog posts are more exciting, though).
Figure 2.10 The blog, lightly styled
Date-Based Ordering
You should have noticed your blog posts are not being presented in traditional reverse-chronological order. It’s easy for us to tell Django to do that; in fact, we have a choice as to where we want to tell it to do so. We can add a default ordering to our model, or we can add it to the BlogPost.objects.all() query in our view code. In this case the model is a better location because we most often want posts ordered reverse chronologically. If we set our preferred ordering in the model, any part of Django that accesses our data uses that ordering.
To set default ordering for your model, give it an inner class called Meta and set the ordering attribute in that class.
class Meta: ordering = ('-timestamp',)
Take a look at your blog home page (/blog/). The newest post should now be on top. The string -timestamp is a concise way of telling Django, “order by the ‘timestamp’ field, and do it in descending order.” (If we omitted the “-”, they’d be presented in ascending date order instead.)
Timestamp Formatting Via a Template Filter
That timestamp is handy, but its ISO8601 format is a little nerdy. Let’s humanize it a bit by using a cool feature of the Django template system: filters.
Because this is a presentation detail, not a data structure or business logic detail, the appropriate place for it is in the template. Open your archive.html file and change the “post.timestamp” line to
<p>{{ post.timestamp|date }}</p>
To apply a filter to a variable, you simply tack it on to the end of the variable name—inside the curly brackets—using a vertical bar, or “pipe,” character. Reload your blog home page. Now your dates appear in a more liberal-arts-friendly form (“July 7 “).
If the default style of the date filter isn’t to your liking, you can pass it an argument using strftime-type formatting. However, rather than using the conversion codes from Python’s time module, it uses the same formatting directives as PHP’s date function. For example, if you want to display the day of the week but omit the year, change the line to pass an argument to the date filter.
<p>{{ post.timestamp|date:"l, F jS" }}</p>
This particular format string gives you dates in the style “Friday, July 6th.” Make sure you don’t leave any space on either side of that colon—the Django template engine is particular about this.