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