Simple Javascript Spell Checking

The solution I chose in the end was Speller Pages, a very simple javascript-driven user interface with a number of different server-side options, each using the GNU Aspell application to do the actual spell checking.

Installation is extremely simple. Download the archive, place it on your web server (under /speller/ seems to be the quickest to get going), edit the JavaScript file to point it at either a Cold Fusion, PHP or Perl back-end, and add some extremely simple JavaScript calls to the page where you want the spell check to appear.

From the users point of view, all they have to do is click the obvious "Check Spelling" button next to the relevant textarea, and a logical, simple to use window appears highlighting the incorrect spellings and providing a list of relevant alternatives. Once they're finished selecting replacement words, the window automatically disappears and the user continues on their merry way - without having to worry about the embarrassment of sending their friends and family to a website full of typos or misspellings.

Total integration time, including making a new spell-check button on the page (with a bit of assistance from the icons by Mark James) , was about fifteen minutes. Not bad for a piece of functionality that makes life just that bit easier to use.

The Pareto principle (Or, why the 80/20 rule always bites us in the ass)

This rant bought to you by yet another unfinished project.

As hard as you work to avoid it, there seems to be no way to avoid the good old 80/20 rule. It seems that no matter what you're working on, 80% of the work takes 20% of the time, and the remaining 20% of the work takes (at least) 80% of the remaining time on the project.

The '80/20 rule', as it's commonly known, is an implentation of the Pareto Principle, originally conceived by observing that 80% of income in italy was received by 20% of the Italian population. (Source: Wikipedia).

In software development this is very visible. It doesn't take a developer long to get a working mock-up online, complete with a basic interface and the most important functionality. But one can almost guarantee that this mock-up will not include some small yet important features that a public, commercial product requires if it's to be used by the masses.

I'm a big fan of Django. But I'm also annoyed at the way it lets us emphasise Pareto's principles. Rapid Web Application Development is the current 'in thing' - using Django or Ruby on Rails, one can get a basic CRUD application online with a minimum of fuss. It works well, it's stable, and it probably has a funky logo and some AJAX magic. There is a good chance it'll have some Google Ads on it to try and cover some hosting costs.

However it'll be missing all the details that make an application truly kick ass. It'll be missing the automatic e-mail notifications that get sent on a monthly basis to subscribers. It'll be missing the extra database fields that make the user experience even better by keeping detailed analysis of user activity to allow the site to be customised to their specific needs. It'll be missing the automated reporting tools that make mundane data truly valuable (both to management types, and to advertisers).

Maybe I'm just impatient, but in this day and age of 'release early, release often' there seems to be too many half-done projects. There are too many almost-there Web-2.0 weblications that, if given that 80% of time, will be something quite new. Earth shattering, even. Something that's ready to make it's developer piles of cash.

It seems there are too many good ideas floating around, but people lose the motivation to get them to that finished and polished stage that makes people say wow. Of course, there are exceptions - just take a look at Tabblo and Flickr to see how a great idea was turned fantastic with the use of some cool technology. Looking at these two sites makes me wonder how many image sharing and social network sites were started but never made it past the beta stage.

Me? I'm working on an online gift registry service for engaged couples. I'm a victim: the site works (and, I must say, works very well), and couples have successfully used it for their bridal registries, but it needs some work around the edges if I want it to be valuable to anybody other than my own small group of friends. There is no reason it cannot be monetized - but people only pay for quality.

Damn that last 20%.

ChangeManipulators on only part of a model

Django has some cool forms management functionality that takes the hard work out of displaying forms and managing the users input. Unfortunately, the ChangeManipulator expects that all fields in a model will be updated by the ChangeManipulator, and cries about any required/not-null fields that you don't display to the user in the form.

This is a huge issue when you want to display a form to a user that only modifies a very small part of a model. For example, you have a 'product' model with a few boolean fields, and you want the user to only alter the 'description' field with a ChangeManipulator. If you were to use the standard ChangeManipulator code (from the above URL) and only displayed the field 'description' to your user, the resulting code would either wipe the values of the boolean fields, or if they're required, complain that they don't have a value.

The fix is simple, and it's been in Django since the new-admin branch late last year. Why it isn't documented, I don't know. It's somewhat explained in Django Ticket # 420.

When you call your ChangeManipulator, there is a second parameter you can use to define which fields will and won't be edited, named 'follow'. It can be used to control multi-level relationships (see the ticket for details) but in this case we want it to ignore a few non-user-editable fields:

follow = {'paid': False, 'package': False, 'reminder_list': False, 'reminder_guests': False, 'security_basic': False}
manipulator = Account.ChangeManipulator(user, follow)

What this will do is ignore the paid, package, reminder_list, reminder_guests, and security_basic fields - and the user won't be prompted with an error message for not entering required fields.

What I'd like to see is a way to exclude all fields (without writing a loop to manually make them all 'False'), and then only include a small subset of fields. In my example, I have three separate screens to edit different sections of the same model - for technical reasons the information is on one model, but for business reasons it's spread over a few screens Using {'*': False, 'editthis_field': True}_ would kick ass.

Housekeeping

I've made some minor changes to my website over the past few days in an effort to make life easier:

  • Added a 'summary' to each blog item, meaning the RSS feed doesn't fill up the Django website, and my front page stays clean
  • Changed to a template sourced from Open Web Design

Sending E-Mails via Templates

So you've got an application written in Django that needs to send large bodies of e-mail, but you don't want the e-mail message itself to be in your Python code. Fair enough, I'd say - you should be separating form from function, and in this case, the e-mail output is still what I'd classify as 'form'.

One way to tackle this situation is to create a template for your e-mail body, process that template to fill in the gaps (eg Username, URL's, etc) and shoot it off via Django's e-mail functions instead of rendering it in a web browser as you'd normally do with templates.

First things first - create your template. In this case I'm writing an e-mail to a user thanking them for registering on a website. I've put the template in my templates directory, called email.txt in the registration subdirectory.

Dear {{ name }},

Thank you for signing up with {{ product_name }}.

Your new username is {{ username }}, and you can login at {{ login_url }}. Once logged in, you'll be able to access more features on our website..

We hope that {{ product_name }} is of good use to you. If you have any feedback, please respond to this e-mail or submit it to us via our website.

Regards,

{{ product_name }} Administration
{{ product_url }}

Pretty simple huh? We're using the same syntax used by standard Django templates, where variable names are enclosed in double-curly-braces.

Next, in your code where you want to send the e-mail, load and compile the template and template context:

from django.template import loader, Context

t = loader.get_template('registration/email.txt')
c = Context({
    'name': new_data['first_name'],
    'product_name': 'Your Product Name',
    'product_url': 'http://www.yourproject.com/',
    'login_url': 'http://www.yourproject.com/login/',
    'username': new_data['username'],
})

What this will do is load your e-mail template into memory, and save it in an object t. Next, it creates a template context, c, with 5 variables in it. These variables share the names in the template above - and in this example, three of them (product name and the two URL's) are hard-coded, and the other two come from a dictionary called new_data.

Lastly, we need to join the context and the template together, and send it off in an e-mail:

from django.core.mail import send_mail

send_mail('Welcome to My Project', t.render(c), 'from@address.com', [new_data['email']], fail_silently=False)

Most of this is pretty straightforward - the e-mail subject will be Welcome to My Project, and you've defined a from e-mail address and recipient (again from the newdata_ dictionary). The funky business is the rendering of the template. The t.render(c) portion simply tells Django, that for template t (defined above), it should use the context c (also defined above), substitute the variables, and return the output. This output becomes the body of the message that the user receives:

Dear Ross,

Thank you for signing up with Your Product Name.

Your new username is rossp, and you can login at http://www.yourproject.com/login/. Once logged in, you'll be able to access more features on our website..

We hope that Your Product Name is of good use to you. If you have any feedback, please respond to this e-mail or submit it to us via our website.

Regards,

Your Product Name Administration
http://www.yourproject.com/

Good luck!

Using Django's TemplateTags

I've had a number of e-mails about how I include the listing of blog tags and archives by month on the side of my website from people who have obviously built up a blog and now want further integration with their website.

Well, it's ultra-simple thanks to a nifty Django feature called template tags. The concept behind template tags is simple - a quick snippet of code in your page templates calls some python code behind the scenes, which does some stuff, and returns either raw HTML code (yuck) or sets new variables in your template context, allowing you to manipulate and display them as you please (cool!)

To get started, in the application directory for your blog, create a directory named templatetags and place an empty file in it, named _init.py_. Now, create a file - in this case we'll call it blogmonths.py_. We need to do a few things in this file:

  1. Import the relevant models so we can access the data
  2. Create and register new template tag
  3. Write the function(s) for that tag so they add data to the template's context.

The contents of this file need to be:

from yourproject.blog.models import Tag,Post
from django.template import Library,Node

register = Library()

def build_month_list(parser, token):
    """
    {% get_month_list %}
    """
    return MonthMenuObject()

class MonthMenuObject(Node):
    def render(self, context):
        context['blog_months'] = Post.objects.dates("date", "month")
        return ''

register.tag('get_month_list', build_month_list)

The important bits of this code are the registration of the tag, and the MonthMenuObject updating context[] and then returning nothing at all.

The context that is set is another Django shortcut - it'll return a date object for each unique month that has a post in it, based on the 'date' column in our Post model. Neat.

Next, create a new file named, for example, blogtags.py_ and paste the following into it:

from yourproject.blog.models import Tag,Post
from django.template import Library,Node

register = Library()

def build_tag_list(parser, token):
    """
    {% get_tag_list %}
    """
    return TagMenuObject()

class TagMenuObject(Node):
    def render(self, context):
        output = ['']

        for blogtag in Tag.objects.all():
            number = blogtag.post_set.count()
            if number >= 1:
                output.append(blogtag)

        context['blog_tags'] = output
        return ''

register.tag('get_tag_list', build_tag_list)

As you can see, this is very similar to the months list - except it checks to see if each tag has any posts, and if it does it adds that tag to a list. That list is then set in the template context for later use.

And that's the hard bit!

Back in your base.html template, choose where you want your list of months to be, and drop in the following code:

<ul>{% load blog_months %}{% get_month_list %}
{% for month in blog_months %}<li><a href="/blog/{{ month|date:"Y/M"|lower }}/" title="{{ month|date:"M Y" }}">{{ month|date:"M Y" }}</a></li>
{% endfor %}</ul>

That'll load up your .py file, execute the tag registered as getmonth_list_ (which, according to the code above, will set a context variable called blog_months), then run through each month in the list and add a link to your page in an unordered list. We use the standard django template filter named date to format the date for valid use in the links and in the anchor text.

Next, choose where you want your tag list and add this (strikingly similar) block of code to the template:

{% load blog_tags %}{% get_tag_list %}
>ul<
{% for tag in blog_tags %}{% if tag.slug %}>a class="link" href="/tag/{{ tag.slug }}/" title="{{ tag.description|escape }}"<{{ tag.title }}>/a<>/li<
{% endif %}{% endfor %}>/ul<

Just like the month block, that runs your new template tag then turns the Python list into a nicely formatted list of tags.

And that's it - you've now got your blog integrated into your website in areas where the blog isn't actually being loaded via generic views - those links are available anywhere in your site!

A Django Blog: Redux

At the start of May, the Django magic-removal branch was merged with 'trunk' - the mainstream Django code base. While this branch isn't yet available for download by the lay user, it's there in svn ready to be checked out and used, and it seems quite stable - the djangoproject.com website was recently moved across to that very codebase.

All of the documentation on djangoproject.com refers to the MR codebase, and because trunk had been updated it meant if I wanted any updates to Django (I run off of the SVN codebase, not the mainstream download) I'd have to go MR. With the 'RemovingTheMagic' wiki page open in one brower screen, I got to making the required changes in another screen. What you're seeing here is the end result of that.

As such, most of the code has changed. Instead of writing a blog entry with a few hundred lines of python code, I've put it all in an archive you can download from my site.

This code is released under a Creative Commons License, as linked with the button on the right side of this blog. Please respect this license - it's very generous. I just want this code to be used as a learning tool, not a money making scheme for somebody :)

I don't intend for this code to be just dumped on a website and used as-is - I expect you to be using it as a learning tool, as a base for bigger and better things. As it is in the archive, it's very basic. The templates aren't complete, they require you to already have a 'base.html' template. This archive also expects you can create a new app in Django and copy these files to the right places.

Good luck, and enjoy your Django experience! I will be making further posts in the future regarding template tags, RSS feeds and other such niceties, which will be based off this code archive.

That's it for now - go and download django-blog-rossp.org.tar.gz.

XSS Vulnerability

A week or so ago I received an e-mail from a nice new zealander, Simon Greenhill, alerting me to a cross-site scripting vulnerability in the comments portion of my blog. The vulnerability will actually probably be of concern to anybody using the comments module from Django - I haven't dug far enough into it as yet to confirm exactly what's at risk.

If you read on, you can see the contents of the e-mail he sent through to me. Basically, the 'name' field from the comments needs to be escaped at the time when the comment preview is displayed. I patched my code some time ago, but now I've got the chance I'm making it public to help everybody else out.

His e-mail said this:

However - I've noticed one problem - your comment preview form is at risk of XSS. If I enter javascript into the name field ( e.g. <script>alert( 'hi');</script> ), it'll be executed. Easily fixed escaping - change this line:

<p>Posted by <strong>{{ comment.person_name }}</strong></p>

to:

<p>Posted by <strong>{{ comment.person_name|escape|urlizetrunc:"40"|linebreaks }}</strong></p>

If this isn't caught at the preview form stage, then it's going to affect your comments listing too since, again, comment.person_name isn't escaped.

The things in comment_form don't seem to be susceptible to this, so I think Django's manipulator's are taking care of the form fields.

So, if you've copied my comments code off this site, it's probably worth making that change for now, until at least Django automatically handles this (to be honest, I thought django.contrib.comments would automatically do this - but I'm sure there's a reason against it) or I can find a 'better' way around it.

Django: Magic-Removal Upgrade

I've just upgraded Django to the new Magic-Removal code base which is now a part of the core Django distribution.

I broke a few things (such as the Photo Gallery) for a few days while I ironed out a few little bugs, but all seems OK now.

Soon enough I'll make available all my blog code as the tutorials published here are now basically useless due to the sweeping changes made by Magic-Removal. If anybody can suggest an easy way for me to package up my Django code to make it easily usable for others, I'd love to hear it :)

I might also pull out some text from previous blog entries on using Django's contrib.comments and put them in their own article as that seems to be what's attracted a number of people to this site.

Building A Blog with Django #3

NOTE: The Python code in this tutorial no longer works with Django! Please read my new article, a Django Blog Redux, for code that works on newer versions of Django. The rest of this article, such as the theory, is still very much applicable and should be read alongside my newer code.


In my two previous posts on this topic (Part 1, Part 2), I walked through creating a simple Blog application using the utilities provided by Django.

In this, the third part of the guide, I will show you how to let your visitors leave comments on each of your postings using the Django comments framework. Then, I'll introduce a quick template and urlconf to let you see all of the posts associated with a given tag, so that your users can pick a subject and see what other posts are related to it.

I'm going to just assume you've already got Django running, and the previous two parts of this guide completed and working. This section just builds on what has already been done.

Adding Comments To Your Blog

Django has a very cool (but undocumented, other than a line saying "We have a comments system that's easy to use!") comments system, that really is quite easy to use, it's just difficult to figure out to begin with. The comments system is pretty much plug-and-play, and has two modes to run in. The first mode only allows registered users to leave comments. The second mode allows anybody to enter their name and leave comments. In this case, I don't use public user accounts, so will not be able to use the first option. This leaves me with the 'free comments' - an option that is very easy to use, but beware of spammers leaving dud links (you can easily delete them via the Admin screens).

Open up your urls.py file, and somewhere in the urlpatterns section add the following line:

(r'^comments/', include('django.contrib.comments.urls.comments')),

All this does is tell Django's URL parser that any requests for 'comments/*' should be sent to another url configuration file, in django/contrib/comments/urls/comments.py. Don't worry, this file is a part of Django, there is nothing else you have to do to 'turn it on'. Now open up your settings.py file, and add 'django.contrib.comments' into your INSTALLED_APPS. From the command-line, run 'django-admin.py install comments' to initialise the comments application. Note: Newer versions of Django don't use django-admin.py, instead type 'python manage.py install comments'.

Next, a number of templates need to be created to be used by the comments system. In your templates directory, create a new directory named 'comments' - this is the name Django will expect to find, and will be searching for. Inside this folder you need two templates: One to show a preview of a new comment, and one to thank the user for posting their comment (along with a link to take them back to where they came from). The first of these, the comment preview, has to be named free_preview.html. In my case this contains the following:

{% extends "base" %}
{% block content %}
<h1>Preview your comment</h1>
<form action="../postfree/" method="post">
{% if comment_form.has_errors %}
    <p><strong style="color: red;">Please correct the following errors.</strong></p>
{% else %}
    <div class="comment">
    {{ comment.comment|escape|urlizetrunc:"40"|linebreaks }}
    <p>Posted by <strong>{{ comment.person_name }}</strong></p>
    </div>

    <p><input type="submit" name="post" value="Post public comment" /></p>

    <h1>Or edit it again</h1>
{% endif %}

{% if comment_form.person_name.errors %}
    {{ comment_form.person_name.html_error_list }}
{% endif %}
<p><label for="id_person_name">Your name:</label> {{ comment_form.person_name }}</p>

{% if comment_form.comment.errors %}
{{ comment_form.comment.html_error_list }}
{% endif %}

<p><label for="id_person_name">Comment:</label><br />{{ comment_form.comment }}</p>
<input type="hidden" name="options" value="{{ options }}" />
<input type="hidden" name="target" value="{{ target }}" />
<input type="hidden" name="gonzo" value="{{ hash }}" />
<p><input type="submit" name="preview" value="Preview revised comment" /></p>
</form>

{% endblock %}

This template looks slighty busy but it really isn't. If the user has posted a valid comment (eg, they have included a name and comment with no errors), the comment is displayed to them with a button saying 'Post public comment'. Clicking this button saves the comment and shows them the 'thank you' page. Underneath the comment preview is another form used to let the user edit their comment, and if there are any errors in their submission, they'll be included in here. Clicking 'Preview revised comment' takes them back to the preview page, along with the updated comment.

Next is the 'thank you' page that confirms their comment has been posted. This page will have access to an 'object' variable for the actual content that prompted the comment to be posted, in this case the blog posting. We simply pick up the object and use it to send the user back to where they came from. Create a file named posted.html in your templates/comments/ folder and drop the following into it:

{% extends "base" %}

{% block content %}

<h1>Comment posted successfully</h1>

<p>Thanks for contributing.</p>

{% if object %}
<ul>
<li><a href="{{ object.get_absolute_url }}">View your comment</a></li>
</ul>

{% endif %}

{% endblock %}

Simple, huh? That's the behind the scenes pages setup and ready to go. Now all we need to do is give the user a way to add a comment to a particular post.

Doing this just requires editing your existing blog templates. Right now, your postsdetail.html_ templete simply shows the body of the post, and an image if one exists. Underneath the detail of the blog post in the template (eg under the block that displays the tags used for this post, but before the end of the block), add the following code:

{% load comments %}

{% get_free_comment_list for blog.posts object.slug as comment_list %}
<h2 id="comments">Comments</h2>
{% for comment in comment_list %}
        <div class="comment" id="c{{ comment.id }}">
                <p><b>{{ comment.person_name }}</b> commented, on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}:</p>
                {{ comment.comment|escape|urlizetrunc:40|linebreaks }}
        </div>
{% endfor %}

<h2>Post a comment</h2>
{% free_comment_form for blog.posts object.slug %}
{% endblock %}

What this will do, is firstly load the comments module into your template, then fetch a list of comments for the current page, saving the list into a variable called comments_list. Then, using a for loop, each of the comments is printed with some nice formatting. Lastly, right at the end of a page, we use a django tag called freecomment_form_ to display a form to the user, letting them enter a comment. That's all there is to do, Django does the rest. Fire up your blog, look at a post, and you'll see the comment posting form. Enter something in there, confirm it, and return to that post - wallah, your comment has appeared!

Listing Posts by Tag

This last part of the article shows you how you can quickly list all the articles for a given tag. I've got this in use here on my site, you can see for example all posts related to Django.

To do this, we'll use a Django Generic View. We will be doing a 'detail' view of a tag, and then in the template, we can fetch a list of associated posts. To get started, open up your urls.py and, near the top of the file, add in the following dictionary:

tag_dict = {
        'app_label': 'blog',
        'module_name': 'tags',
        'slug_field': 'slug',
}

Next, further down the urls.py, add the following to your urlpatterns:

(r'^tag/(?P[A-Za-z-_]+)/$', django.views.generic.list_detail.object_detail', dict(tag_dict, slug_field='slug')),

What this will do is tell Django that any request for /tag/django/ should be sent to the generic object detail view, using application 'blog', module 'tags', and slug field 'slug' within that module. All that's left now is to create a template for this: In your templates/blog/ directory, create a file named tags_detail.html and paste into it the following:

{% extends "base" %}

{% block content %}
<h1>Posts with tag '{{ object.title|escape }}'</h1>
{% for post in object.get_post_list %}
        <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>

        {% if post.image %}<img align="right" src="{{ post.get_image_url }}" /> {% endif %}

        <p>{{ post.body|truncatewords:80 }}</p>

        <div class="article_menu"><b>Posted on {{ post.date|date:"F j, Y" }}</b> <a href="{{ post.get_absolute_url }}#comments">View/Add Comments</a><br />Tags: {% for tag in post.get_tag_list %}{% if not forloop.first %}, {% endif %}{{ tag.title }}{% endfor %}</div>

{% endfor %}
{% endblock %}

What this does is pretty straightforward: it gets a list of posts with the selected tag, and displays them in the familiar format. Clicking a post title will display that post (including, if you completed the steps above, the comments for that post).

That's all there is to it! In the next post, I'll cover the addition of RSS feeds to your blog (including a site-wide feed, as well as a per-tag feed). After that, I'll post an article on using Django's template tags to do a sidebar-style menu that contains a list to each of your tags, as well as a link to each month that contains posts. Best of luck until then!

Engagement Party Photos Done

I've just put the rest of the Engagement Party photos online. Some are a bit dark & blurry, but we'll remove those later. Captions will also come later.

Building a Blog with Django #2

NOTE: The Python code in this tutorial no longer works with Django! Please read my new article, a Django Blog Redux, for code that works on newer versions of Django. The rest of this article, such as the theory, is still very much applicable and should be read alongside my newer code.


In the previous installment, I covered my database model for this weblog and explained how to get the basic database layout working with the nice Django administration panel.

In this posting I will show you how to actually display your blog posts to your website visitors. I'll assume you already have a webserver working, with Django installed, and I assume you've already added a few blog posts using the admin tool discovered in part 1.

The first step is telling Django what our URL structure will be. Django does this in a very cool way - it lets us use regular expressions to define what URL paths will be used on our website, and what Python code will be called by users requesting those addresses. To keep this clean I will move all of the blog URL configuration into it's own file.

Let's tell Django to use this file. Edit your main urls.py file and add the following inside the urlpatterns section:

(r'^blog/', include('myproject.apps.blog.urls')),

This tells Django to look at the file 'myproject/apps/blog/urls.py' to complete the URL matching. So, open that file up and throw in the following:

from django.conf.urls.defaults import *  
blog_dict = {  
    'app_label': 'blog',  
    'module_name': 'posts',  
    'date_field': 'date',  
}  

urlpatterns = patterns('django.views.generic.date_based',  
    (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[0-9A-Za-z-]+)/$', 'object_detail', dict(blog_dict, slug_field='slug')),  
    (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$',  'archive_day', blog_dict),  
    (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$',  'archive_month', blog_dict),  
    (r'^(?P<year>\d{4})/$',  'archive_year',  blog_dict),  
    (r'^/?$',  'archive_index', blog_dict), )

This urlpatterns setup tells Django to use the built-in date-based generic views. These views let us avoid writing logic in Python to fetch, order, and manipulate data. The above code gives us a number of functions, all accessible from different URL's. There are 5 different requests that can be made, and they all relate to the data in 'blog_dict' - that is, we are looking at the application named 'blog', and within that app the data model called 'posts'. Lastly we tell it to use the field named 'date', inside the 'posts' model, to do our date sorting and breakdowns. In reverse order, the URL's above provide us with the following:

  • Accessing /blog/ will show a list of all posts. Ordering will come from our data model - that is, by reverse date order
  • Accessing /blog//, eg /blog/2006/ will show a listing of posts for just that year. In our case, as you'll see in the template later, we will just show a listing of months within that year that contain posts.
  • Accessing /blog///, eg /blog/2006/jan/ will show a listing of posts for that month.
  • Accessing /blog////, eg /blog/2006/jan/12/ will show all postings on that particular day
  • Accessing /blog//// will show a particular item. For example, the slug for my previous post titled 'Stock Photos & Web Templates' was 'stock-photos-web-templates', and the article was posted on January 12, 2006. The URL for this post will be /blog/2006/jan/12/stock-photos-web-templates/. Later, at this URL we will allow users to post comments.

As you can see, those few (relatively) simple lines provide us with quite a bit of very useful functionality. Now all we need to do is display the content when users hit the above web addresses.

First things first, lets create a basic template. All this template will do is hold together the structure of our page.

Edit your settings.py file, and add a variable (near the bottom is OK) called TEMPLATE_DIRS. It should look like this:

TEMPLATE_DIRS = (  
    '/path/to/your/application/templates',  
)

Create a new folder in your application called 'templates', and alter the above path to point to that folder. In that folder, create a file called 'base.html', which will be our plain web template. You can spice it up later. Throw the following into that file:

<html>  
<head>  
<title>My First Django Blog</title>  
</head>  
<body>  
<h1>My First Django Blog</h1>  
{% block content %}Content will go here{% endblock %}  
</body>  
</html>

As you can see this is pretty straightforward HTML, except for one bit: the concept of blocks. This content block will be inserted later by our other templates.

When we use the Django generic views, it expects templates to be in a certain place. It will go through your TEMPLATE_DIRS listed above, looking for a folder named the same as the 'app_label' you defined in urls.py. Hence, create a folder in your template directory called 'blog'. We will put all the blog templates in this folder.

Now we will create the first template: the index. Because we're not yet drilling-down by date, this view is simply called an 'archive' view (reflected in urls.py - called 'archive_index'). Django's generic view templates are named by the model name, followed by the view name. In your 'blog' folder, create 'posts_archive.html' and paste into it the following:

{% extends "base" %}  

{% block content %}  
<h2>Blog: Latest Postings</h2>  
{% for object in latest %}  
        <h2><a id="{{ object.slug }}" xhref="{{ object.get_absolute_url }}">{{ object.title }}</a></h2>  

        {% if object.image %}<img align="right" xsrc="{{ object.get_image_url }}" /> {% endif %}  

        {{ object.body|truncatewords:80 }}  

        <div class="article_menu"><b>Posted on {{ object.date|date:"F j, Y" }}</b> Tags: {% for tag in object.get_tag_list %}{% if not forloop.first %}, {% endif %}{{ tag.title }}{% endfor %}</div>  

{% endfor %}  
{% endblock %}

As you should be able to see, this template will load up your base.html file, and substitute the content block for the one in this file. Django will pass through an array of objects called 'latest'. We run through this group of posts, each time assigning one to the variable 'object'. We then display a snippet of HTML for this post, with the title (linked to the full post), a truncated body (cut off at 80 words - the reader can click the title to read the rest of the post), the posting date, and the tags assigned to this post. Simple!

Now if you visit your /blogs/ URL you should have a list of any postings you added (in part 1, via the Admin screen).

The next step up the list is to list by year. In your blogs template directory create posts_archive_year.html (which is what the generic view will look for):

{% extends "base" %}  
{% block content %}  
<h2>Blog: {{ year }} Archive</h2>  
<ul>  
{% for date in date_list %}  
        <li><a xhref="{{ date|date:"M"|lower }}/">{{ date|date:"F" }}</a></li>  
{% endfor %}  
</ul>  
{% endblock %}

Next, posts_archive_month.html:

{% extends "base" %}  
{% block content %}  
<h2>Blog: {{ month|date:"F" }} {{ year }} Archive</h2>  
{% for object in object_list %}  
        <h2><a id="{{ object.slug }}" xhref="{{ object.get_absolute_url }}">{{ object.title }}</a></h2>  
        {% if object.image %}<img align="right" xsrc="{{ object.get_image_url }}" /> {% endif %}  
        <p>{{ object.body|truncatewords:80 }}</p>  

        <div class="article_menu"><b>Posted on {{ object.date|date:"F j, Y" }}</b></div>  
{% endfor %}  
{% endblock %}

And posts_archive_day.html (pretty much identical to the month view!).

{% extends "base" %}  
{% block content %}  
<h2>Blog: {{ day|date:"F j, Y" }} Archive</h2>  
{% for object in object_list %}  
        <h2><a id="{{ object.slug }}" xhref="{{ object.get_absolute_url }}">{{ object.title }}</a></h2>  
        {% if object.image %}<img align="right" xsrc="{{ object.get_image_url }}" /> {% endif %}  
        <p>{{ object.body|truncatewords:80 }}</p>  

        <div class="article_menu"><b>Posted on {{ object.date|date:"F j, Y" }}</b></div>  
{% endfor %}  
{% endblock %}

Lastly is posts_detail.html - this will display a single post.

{% extends "base" %}  
{% block content %}  
<h2>Blog Entry</h2>  
{% load comments %}  
{% get_free_comment_count for blog.posts object.slug as comment_count %}  
<h3><a id="{{ object.slug }}" xhref="{{ object.get_absolute_url }}">{{ object.title }}</a></h3>  

{% if object.image %}<img align="right" xsrc="{{ object.get_image_url }}" />{% endif %}  

<p>{{ object.body }}</p>  

<div class="article_menu"><b>Posted on {{ object.date|date:"F j, Y" }}</b> Tags: {% for tag in object.get_tag_list %}{% if not forloop.first %}, {% endif %}{{ tag.title }}{% endfor %}  
</div>

You now have a working blog application that allows you to quickly drill-down from year to month to day, showing just a particular subset of results.

In the next article, find out how to see a list of posts for a given tag, and how to let your users leave comments for you (guess what: it's real easy, thanks to Django!)

Engagement Party Photos

On Sunday afternoon Lauren and I had our Engagement party to celebrate with friends & family our recent engagement. Not all of the photos are up yet, but the first batch are ready to look at.

The photos can be found in my gallery.

Gallery is Online Again

I've just put parts of my photo gallery back online, using a new gallery application I wrote in Django. I've only got a few sets of photos online right now, the rest should be ready to go shortly.

You'll see with the sets I have there so far, the locations are drawn on a satellite photo of Australia, thanks to Google Maps. Try zooming in on some of the places, especially around Melbourne. They are quite high resolution images and it's fascinating what you can see.

Building a Blog with Django

NOTE The Python code in this tutorial no longer works with Django! Please read my new article, a Django Blog Redux, for code that works on newer versions of Django. The rest of this article, such as the theory, is still very much applicable and should be read alongside my newer code.


When I first posted about Django, I said that I'd post the details of how I wrote a blog in Django, without actually writing any real Python code. This will show you how you too can have a simple blog working in no time at all.

I'll assume for this article that you've got Django installed and working, and you know the basics. Create a new project and within it create a 'blog' application. Edit the models file for that application, so it contains the following text:

from django.core import meta

class Tag(meta.Model):
    slug = meta.SlugField(
        'Slug',
        prepopulate_from=("title",),
        help_text='Automatically built from the title.',
        primary_key='True'
    )
    title = meta.CharField('Title', maxlength=30)
    description = meta.TextField(
        'Description',
        help_text='Short summary of this tag'
    )
    def __repr__(self):
        return self.title
    def get_absolute_url(self):
        return "/tag/%s/" % self.slug
    class META:
        admin = meta.Admin(
        list_display = ('slug', 'title',),
        search_fields = ('title', 'description',),
    )
class Post(meta.Model):
    slug = meta.SlugField(
        'Slug',
        prepopulate_from=('title',),
        help_text='Automatically built from the title.',
        primary_key='True'
    )
    assoc_tags = meta.ManyToManyField(Tag)
    title = meta.CharField('Title', maxlength=30)
    date = meta.DateTimeField('Date')
    image = meta.ImageField(
        'Attach Image',
        upload_to='postimgs',
        blank=True
    )
    body = meta.TextField('Body Text')
    def __repr__(self):
        return self.title
    def get_absolute_url(self):
        return "/blog/%s/%s/" % (self.date.strftime("%Y/%b/%d").lower(), self.slug)
    class META:
        admin = meta.Admin(
            list_display = ('slug', 'title', 'date'),
            search_fields = ('title', 'description'),
            date_hierarchy = ('date',),
        )
        ordering = ('-date',)

What this creates for us is three database tables. The first, which will be called 'blog_tags' (a concatenation of the application name, and the pluralisation of the class name) will contain three fields. First is a 'slug', which is an alphanumeric representation of the title (and our primary key). This will be automatically generated from the second field in this table, the title. If the title is, for example, "How To Make A Nice Coffee", the slug will be "how-to-make-nice-coffee" - all in lowercase, with hyphens for spaces, and common short words (such as "a" and "at") removed. Lastly is a description field, which we'll use later in tooltips.

For this tags table, we also define a few functions. The first, repr will return the name of the category by default when ask for a basic view of that category - this makes it easier to use this model later on. We also define a function called 'get_absolute_url' - this is used for us to quickly build a link to view this tag outside of the admin - in this case, we will link to /tag/tag-slug-name/. Later on we will build a template that, when called via this URL, will display a list of articles that belong to that tag. Lastly we define some simple Admin stuff, which I won't detail here.

The second table to be created is called 'blog_posts' and will contain a few simple fields. Again, a slug and title, same as the 'tags' table. It will also contain a ManyToMany field, relating it to the 'tags' table. This lets us select multiple tags for each post, providing a quick and easy way to categorise blog postings without having them tied into only one category. There is also a date field (which, in the Django admin will provide pop-up calendars and time selectors... cool!), an Image field (which lets you attach an image to a post), and a field for the body of the text. Again, we define a repr and basic Admin functions, and a get_absolute_url function: This time the URL will refer to /blog/year/month/day/slug/, which allows each post to have a simple-to-read URL that will never change.

Save your changes, and then edit your 'urls.py' file. Ensure the admin line is uncommented:

(r'^admin/', include('django.contrib.admin.urls.admin')),

Now, visit your /admin/ url, for example http://mydomain.com/admin/. Login with your Django username and password, and you'll see 'Tags' and 'Posts' options in the list. Add a few tags in, noting that the slug is built automatically. Add a few posts, noting you get nice widgets for selecting multiple tags, for picking dates, and building slugfields.

In the next article, find out how we can use Django's generic views to display blog postings, including a date-based archive, by writing practically zero code. Stay tuned, and in the meantime see if you can tweak the above model a little, and learn how objects are represented in the Admin screens.

Stock Photos & Web Templates

Two links I try and use regularly are sources of free web templates, and free stock photography.

stock.xchng is a website that has over 180000 photos, of everything you could imagine. Most of them are free for various uses, worth signing up for and looking around. You can easily find an image for just about any purpose. The website is a little slow, but it's worth waiting.

Open Web Design is a source of free web templates. They are offered under various licenses, just check for each one as to what you can/can't do with it. A few designs get put up each day, and there are many to chose from (like this site - I've borrowed parts of a few different layouts to get what I wanted). Very cool for those of us who know what we want but can't design for shit.

Django: My First Impressions

Until last week I had never heard of Django, a web framework written in Python. I'd seen python before, but had never really tried it out - I could do everything I wanted to do in PHP, and having libraries such as Smarty available to me solved all my problems.

Or so I thought.

I thought I'd give Django a shot as I'd seen it touted as the Ruby on Rails killer in Python. I downloaded it, installed it on UGBox, enabled mod_python and walked myself through the Django tutorial. Not 15 minutes later I had a working application, but I'd written bugger all code.

This inspired me. Django has a publishing background - it was originally written for use by a number of newspapers and websites in Lawrence, Kansas. I've been working on a website for a little while now (which is still not live - it'll be ready soon, and is aimed at Blokes in Australia) which follows a similar publishing model - articles, authors, categories, comments... pretty straightforward stuff.

I got to work writing a simple data model in Django during my lunchbreak last Friday. In one file I set out some basic details for my database - Django makes this so easy it shouldn't be allowed - after creating a simple model, Django automatically initialises the database and creates the appropriate fields.

Once the database was in place, Django's flexibility really sunk in. I installed the 'admin' application that is included with Django - and my world was turned upside down. Instantly, a beautiful back-end admin screen was built for me to add articles, link them to categories, edit data, create data, and more. After 15 minutes writing up some templates, the public side of the website was done - and I had a fully operational news publishing website in less than an hour.

At this stage, the website was functioning identically to what I'd already written in PHP - except the PHP code took me a month to write, and this took a lunchbreak. Why hadn't I discovered this in the past? Obviously I need to do some cleaning up, but the core of the application is there, and it just works.

Next on my little todo list, to get my hands-on experience with Django increased, I decided to write this blog. I did this over my weekend - and noticed something funny. For this entire blog, the only bit of code I wrote was the data model, and then some paramaters for the RSS Feeds. No logic was needed, no data fetching, no linking - just a few tags in my templates to make everything show in the right place. Un-friggin-believable.

I'll be posting some of my code up shortly to show how simple this stuff really is, with such amazing results. Until then, I'm off to convert a few more of my little PHP apps into Django - in the meantime I suggest everybody who writes web apps should check out the Django tutorial today.

Getting Started With Django

I've just written this quick weblog app using Django - a new(ish) web framework written in Python.

So far it seems pretty damn good - I've also written an article management system for a new website I'm starting, and it only took a lunchbreak to get going. Amazing. I'll post some more detail up soon.

More...

Want to see more? Check out the yearly archives below.