Creating the Weblog Application
This is part 4 of a series of posts on James Bennett's excellent Practical Django Projects. The table of contents and explanation can be found here. Before we start going through the Weblog app, you may want to have a look at my post on Django tests. This will help sort out any basic errors you make typing up examples.In this section of the book James gives some useful tips for organising your project files, which are worth taking on board. I also recommend checking out Eric Florenzano's screencast on the topic. Once you get to the point of needing to maintain different combinations of versions of packages you'll want something more like zc.buildout or virtualenv but for now symlinks or altering your PYTHONPATH is going to be fine.
Designing the Models
On p47 we design the Category model. As usual remove the Admin class and create your own admin.py. If you want to do tests as well go ahead and create tests.py as well#admin.py from django.contrib import admin from coltrane.models import Category admin.site.register(Category)
#tests.py from coltrane import admin, models, viewsAdd coltrane to the installed apps as described on p48 and then run python manage.py test to ensure you haven't made any typos. Run python manage.py syncdb and continue with p48.
On p50 instead of adding prepopulate_from=['title'] to your Category model reorganise your admin.py instead to create a custom admin class.
#admin.py
from django.contrib import admin
from coltrane.models import Category
class CategoryAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug":("title",)}
admin.site.register(Category, CategoryAdmin)
Also on page 50 help_text is added. There is a way of overriding this in your admin class but for simple cases you can leave it in the model. I would have thought help_text would have been shifted entirely to forms but I guess there is a case to have some default description in your model to describe the field even if you override it in your form or admin.On p54 the class Entry is updated to include a prepopulated field slug. Update your admin.py for this.
#admin.py
from django.contrib import admin
from coltrane.models import Category, Entry
class CategoryAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug":("title",)}
class EntryAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug":("title",)}
admin.site.register(Category, CategoryAdmin)
admin.site.register(Entry, EntryAdmin)
On p59 the django-tagging app is downloaded.svn checkout http://django-tagging.googlecode.com/svn/trunk django-tagging
On page 60 the save function for the Entry class uses super which is briefly described. Super was one of those things that seems straightforward to me now, but the first time I saw it, it was like some magicians trick where half the trick is hidden from view, but a rabbit gets plucked impossibly from a hat. For the benefit of those new to python (and programming), I'll show a simple example.
# First we'll have our base class which is usually someone elses code you're going to subclass.
class Container(object):
"""Stuff comes out of containers"""
def payoff(self, some_object=None):
if some_object:
return "dove"
return
# Now here's the class we might have written
class Hat(Container):
"""Rabbits come out of hats"""
def payoff(self, some_object=None):
if some_object == "watch":
return "rabbit"
return super(Hat,self).payoff(some_object)
# Here's the example of how we'd use this class
>>> trick = Hat()
# if we put in a watch we get the class we've defined
>>> trick.payoff('watch')
rabbit
# If we put in anything else including nothing at all we get the parent class
>>> trick.payoff('hankerchief')
dove
Pretty straightforward really. I think I originally just got confused by the syntax of passing the name of the class in when you're calling it from the class itself, and the fact that invariably examples I'd seen didn't have the parent class code in front of me.Onto p61 ignore the class Admin, and then on p69 there is a typo in the regex. The last bit should be (?P<slug>[-\w]+)/$'
On p71 the urls.py is re-written to take advantage of generic views. after d{4} on each entry there should be a closing bracket - ) - and the same again for p73. I've added my urls into my imports in tests.py as well.
Alas no templates are available on the apress website, so you'll have to make your own to take advantage of the generic views.
So finally we come to a bit more python magic with decorators. If you want to read further about python's implementation of decorators and what they can do, have a read of this article, but I figured I might as well include another silly example that is similar in spirit to the way it is used in PDP.
def magician(fn):
def new_trick():
print "Cut assistant into bits"
assist = fn()
cut = assist.split()[1]
return [bits for bits in cut]
return new_trick
@magician
def assistant():
name = 'lovely assistant'
print name
return name
# Get the assistant - you can see the order in which things are called.
>>> a = assistant()
Cut assistant into bits
lovely assistant
# Print what is returned by the magician
>>> print a
['a', 's', 's', 'i', 's', 't', 'a', 'n', 't']
So that's it until part 5. Enjoy.