Blog of Jeff A blog about programming and random other things.

13Oct/08link

Beginning to Code

Okay, now we're ready to start writing code! In normal Django development, you want to plan out what data your database will contain, this is important. This is due to one current flaw in Django: that Django lacks a schema evolution system. Django can only reset or create tables, but not modify existing ones.

Once you've planned out your data structures for your database, you can start creating models. Models describe how your data is stored in the database and any extra metadata your application needs to be aware of but the underlying database system may or may not be storing.

For this tutorial, I've already have a set of models designed (with some consideration for features not implemented in this tutorial series). We're going to have 4 model objects:

  • Room: this contains room information, such as name, owner, etc.
  • Message: this contains the actually message a user sends.
  • Membership: this model associates a Room with a User (aka, we know that user can view this chat room)
  • Attachment: this contains file upload information that may be associated to a particular message.

Model definitions are fairly easy to make. Let's make the Room model:

from django.db import models
import datetime
class Room(models.Model):
     name = models.CharField(max_length=200)
     created_at = models.DateTimeField('created at', default=datetime.datetime.now)
     def __unicode__(self):
         return u'%s' % self.name

Most of the code is simple. The first two lines imports the Django Model class that Django provides (which makes model definitions extremely simple) and we import the native python datetime module for, you guessed it, time functionality.

In the Room class definition we simply define properties to that tells Django what type of data each property stores. These properties are mapped as fields in the database.

For example, the name property we set stores a string (char or varchar in database terms) with a max length of 200 characters. The other two properties are just as readable. The first parameter for nearly all Field functions is a human-readable name of the field. In the case for the name property, Django will just use the field name. Also, the __unicode__ method provides a display-friendly output of a particular model (which we're outputting the room name). The friendly names are for Django's automatic admin.

This could be represented entirely in SQL also, but doing so in python is cooler ;)

Now type the following to tell Django to create any tables for models that don't exist for all applications in the INSTALLED_APPS list in your settings.py file:

python manage.py syncdb

You will probably get request to make a superuser, create one for your user authentication system (and automatic admin interface).

Automatic Admin

Now we can implement one of the more exciting features of Django: the automatic administrative interface! Although this application doesn't really need the automatic admin, it's nice for quick debugging still.

Now, in your models.py add the following:

from django.contrib import admin
admin.site.register(Room)

This will tell the admin interface to include your Room model for editing. Then 1... 2... 3... poof, your model is part of the backend interface. You can now add, edit, and remove Room entries by visiting http://localhost/admin/ and logging in (assuming your server is still running).

Editing the Room Model

Before we continue, we're going to add a relationship to a user account (so we know which user created a particular room). So add the following to the Room model:

owner = models.ForeignKey(User)

The foriegn key generates a relational link to another model, in this case, the user model provided by Django. And add this import statement on the top of the file:

from django.contrib.auth.models import User

The statement above includes the User model for use in your models.py file to link to in the foreign key statement.

Unfortuantely, the current flaw in Django is a lack of migration (there are some third-party migrations available, although I haven't tried them yet). So the best way to update the database to reflect changes that you've made is to type:

python manage.py dchat

Of course you should change dchat to whatever you named your application. but be warned. This clears any data you've entered for your tables!

The Other Models

Now we can start writing the other models. Here's the code:

class Membership(models.Model):
	user = models.ForeignKey(User)
	room = models.ForeignKey(Room)
	created_at = models.DateTimeField('created at', default=datetime.datetime.now)
	def __unicode__(self): return u'%s @ %s' % (user, room)
class Message(models.Model):
	room = models.ForeignKey(Room)
	user = models.ForeignKey(User)
	membership = models.ForeignKey(Membership)
	contents = models.TextField()
	created_at = models.DateTimeField('created at', default=datetime.datetime.now))
	def __unicode__(self): return self.user.__str__()
class Attachment(models.Model):
	message = models.OneToOneField(Message)
	name = models.CharField(max_length=100, help_text='The filename displayed')
	url = models.URLField('Public URL', help_text='The url to the file in relation to the UPLOAD_ROOT directory.')
	path = models.FileField(upload_to='uploads/', help_text='Webserver\'s path to the file.')
	created_at = models.DateTimeField('Uploaded on', default=datetime.datetime.now))
	def __unicode__(self): return u'file: %s' % name 

Reading through most of the code, it's pretty much self explanatory if you've seen SQL before. The help_text keyword parameter is used in the Django automatic admin interface to assist administrators in understanding what the field is about.

Admin Interface Customization

Now obviously having something automatic won't generate something you want precisely (unless it could read your mind). Although the administrative interface is very customizable, I'm not going to cover it much since that's not the main purpose of this app. But we'll briefly cover some customization features that we can utilize.

Admin.py

Putting Administrative code in your models.py file can clutter up your original models. This is especially true when you want further customization other than registering your model in Django Admin. Luckily, you can put all your admin stuff into a separate python file which will be automatically read by Django's Admin. So delete all the administrative interface code we placed in models.py and create a new file: admin.py in the same directory.

Now in the admin.py file put the following:

from django.contrib import admin
from dchat.chat.models import *

class RoomAdmin(admin.ModelAdmin):
	fieldsets = [
		(None, {'fields': ['name', 'owner']} ),
		('Date information', {
			'fields': ['created_at'],
			'classes': ['collapse'],
		}),
	]
	list_display = ('name', 'owner', 'created_at')
	list_filter = ['created_at']
	search_fields = ['name', 'owner']
	date_hierarchy = 'created_at'

admin.site.register(Room, RoomAdmin)

This is class definition provides extra information to the Admin interface that isn't necessarily needed in the model itself.

The fieldset property is a list of tuples that tell the admin ui how to display its edit and add fields to the user. The first element in the tuple is the name of the section (aka, fieldset in HTML) to use. In the first tuple, the fields are listed under no visible section. The second element in the tuple is a dictionary of all additional properties to add to the section. one notably is the fields key which contains a list of all the field names of the Room model that we want to show. For the second tuple (with 'Date information') you will notice a classes key. This allows you to assign additional css classes to the section, in this case, I use the collapse class (which is provided by the admin by default) which "folds" the section by default.

If you don't need this complexity of configuring, you can alternatively use the fields property and simple provide a list of strings of all the fields of the model you want visible in the Admin.

The other properties in the ModelAdmin isn't as complex. The list_display property indicates which fields should be visible (and their values) in a table view of various rows. List filter allows the user to filter items by a specific field similar to group drill downs in yahoo/google groups.

The search_fields property lists all the fields that should be used when a user performs a search query. Ideally it is best to keep the number of fields on this at a minimum since Django is simply wildcarding requests to the database.

The date_hierarchy allows the user to drill down a particular date. In this case, it's by the date the room was created.

Next time...

In the next post, we'll be adding a bit more Admin magic before working on views. :) Adios!

  • Share/Bookmark
blog comments powered by Disqus

Recent Posts

Topics

Archives

Following

Links