Thursday, July 15, 2010

Django from Scratch

Today I had the pleasure of creating a brand-new Django project from scratch. Here are some things I realized or remembered during my first couple of hours of work after maintaining a large Django project for over a year.

Create a local_settings.py File
Put it in the project folder and import it from settings.py with exception handling so it's okay if it doesn't exist. Make sure your version control ignores this file. Use this to override your database settings, add plugins (like Django Debug Toolbar), and to set DEBUG = True. Always leave DEBUG set to False in the real settings file. That's just one thing you don't want the slightest possibility of moving into production. Also, don't reverse the process and import settings.py in local_settings.py. It causes problems when you have to remember to set or import the proper DJANGO_SETTINGS_MODULE for any given environment. It's so much easier to just let Django use the default settings file hard-coded into manage.py.

Create Initial Fixtures
During development you'll likely create some data. This is useful for obvious reasons. If you create a fixture file with the name initial_data in a path specified in your FIXTURE_DIRS setting, it will be automatically loaded each time you flush the database, or after a syncdb. At the very least it will save you from re-entering the admin super-user info each time you refresh the database.

Use South
This has become a no-brainer for me. South is a database migration tool for Django which is very mature and has become the de facto standard in the community. As tweaks are made to the models, South makes modifying the database completely painless. It makes things a little easier if you create a South migration right from the start, rather than waiting till you make a change. The documentation is very good and explains all. Tip: When you get ready to deploy for the first time, you can always wipe out your South migrations and make a new "initial" migration. Your migrations folder will be cleaner and it'll look like you designed the models right on the first try.

Use setUp in Your TestCases
I could have also titled this one "test one thing at a time." I spent a lot more time today than I should have trying to figure out how to repair a dirty database connection after one test that (correctly) raised an IntegrityError. In the end, I broke out the following test into another function. In hindsight, there was no point in trying to do them both together, except that I was loading a couple of model instances that I wanted to use for both tests. Creating a setUp function in my test class solved the problem nicely, and is much cleaner. If a test ever modifies one of those instances it could introduce a subtle bug into my test suite.

Create Test Fixtures
Have some test data that your tests load. This may seem obvious, but when I first started writing tests I did it the hard way, creating model instances at the beginning of a test because they were dependencies of the model I actually wanted to test.

Create a Test Directory
A fresh Django app will leave a file named tests.py in the new directory. This is to remind you that testing is important. But a single file can become a mess. I much prefer the technique I learned from Eric Holscher's blog. Delete that tests file and create a tests directory. In that directory, create an __init__.py file. Create a bunch of test files, each serving a different purpose, and import them in __init__.py. That way, they'll be nicely organized and still be run automatically when you run ./manage.py test.

Use the related_name Keyword
In models where you use a ForeignKeyField, always specify a related_name. You're going to use it anyway eventually, so you may as well be consistent. Also, it will make your code (and your models) clearer to the maintenance programmer, who will probably be you. Also, adding one later on could break existing code that was using the default syntax.

Don't Use Sqlite
This one upsets me, because I love sqlite. However, it doesn't support certain constraints, which means tests will fail that would pass on the production database. Worse, some constraints do work, but only when a table is created, not when they're added later by a migration. Also, it doesn't support all the functionality required to use South, like the ability to delete columns from a table.

May your next project be a Django project!

--ShawnMilo

3 comments:

  1. i read with interest your final point. i dont suppose you have a good guide on getting postgresql working on os x? i'm getting to the point where im about to go for a new mac and i;d like to be running virtualenv, pip etc and if you reckon sqlite is too lite then i might go for postgresql.

    ReplyDelete
  2. Shofty,

    I just download and run this installer:
    http://www.postgresql.org/download/macosx

    You shouldn't have any problems if you just run that, unless you're running Snow Leopard, in which case you'll need to recompile Python in 32-bit mode to get psycopg2 (the Postgres module for Python) to work.

    I ran into that problem and wrote about it on this blog:
    http://shawnmilo.blogspot.com/2009/11/psycopg2-on-snow-leopard.html

    ReplyDelete
  3. Awesome! Thanks for sharing this list... It's nice to see these things pointed out explicitly instead of just stumbling into them on every new Django project like I do lol.

    ReplyDelete