Customizing Django startproject with templates

jun 6, 2013 - Alex - django - modwsgi - deployment - Development - Django - Software

When it comes to Django everyone has their own way of structuring a project. The default layout when running startproject is reasonable for smaller projects, but it quickly gets messy when those projects grow. Two Scoops of Django has a whole chapter on the subject, but rightfully states that no two developers would structure their project in the same way.

The default project structure of Django:

<project_root>
|
+-- manage.py
|
+-- <project_app>
|
+-- <another_app>
|
+-- <project_name>
     |
     +-- settings.py
     |
     +-- urls.py
     |
     +-- wsgi.py

As stated this works fine for smaller projects, but once you start adding multiple targets (development, staging, production), documentation, third-party apps, logs and media directories things can get rather cramped.

Thankfully Django allows you to provide a template when starting a new project, giving you the ability to completely customize your project structure. We recently opened our internal 'default-project' that we use as a template, you can find it at https://bitbucket.org/maykinmedia/default-project

Using a project template is very easy. With django-admin.py installed and wanting to start the 'foobar' project, simply do the following:

django-admin.py startproject --template=https://bitbucket.org/maykinmedia/default-project/get/tip.zip --extension=py,rst,rb,html foobar

This is how our 'foobar' project would be structured:

foobar
|
+-- env                 -- Virtual environment files.
|
+-- src                 -- Container for one or more source directories.
|   |
|   +-- foobar
|       |
|       +-- conf        -- Django settings files for various targets (production, staging, development)
|       |
|       +-- templates   -- Project templates
|       |
|       +-- wsgi        -- Default location for wsgi deployment scripts for various targets
|       |
|       +-- static      -- Default location for project static files
|       |
|       +-- ...         -- Project specific applications.
|
+-- static              -- Default location for collected static files.
|
+-- media               -- Default location for uploaded media files.
|
+-- log                 -- All log files generated by the project.
|
+-- doc                 -- Documentation source and generated files.
|
+-- requirements        -- Project requirements for each type of installation.

We also provide a 'bootstrap.py' script with which we can initialize our environment. Besides running virtualenv it also makes sure we are using the correct settings file for our current target:

python ./bootstrap.py development

or

python ./bootstrap.py production

Now every time we activate our virtualenv (source ./env/bin/activate) it makes sure to use the appropriate settings file.

Our default-project structure ensures all our code is separated from any non-code files, as all our code is isolated in the ./src/foobar directory. Third-party forks are placed alongside this directory, for instance in ./src/django-mptt

The placement of a projects virtualenv directory is another topic for discussion. Some developers prefer to place it in ones homedirectory (eg. ~/.venv/foobar) and others use their project root-directory as the base of the virtualenv. We prefer to keep it isolated in the ./env/ directory: this way the project is contained (the virtualenv is the same across multiple users per deployment) and it is easy to remove your virtualenv and rebuild it when you run into dependancy problems.

For each target (production, staging, development) we prefer to have a separate settings file and a separate wsgi file. This might be overkill for some, but the main benefit is that everything (besides your secret variables!) can be placed in version control. Settings that are the same across targets go into the main 'settings.py' file, but if you want to install django-debug-toolbar only on your development environment you can simply add it to 'settings_development.py', ensuring it doesn't interfere with your customer-facing deployments. This is also the reason we have a separate requirements-file per target. Each references 'base.txt' but this allows changes between targets.

Managing your WSGI files sounds simple, but can also get rather messy. Our default-project contains a couple of tricks to make your life easier: you don't have to set environment variables and your virtualenv is automatically prepended to your sys.path. This ensures that your wsgi-enabled webserver simply has to call the appropriate wsgi_<target>.py file and host your /media and /static directories and avoids hacked-together wsgi files that tend to break for obscure reasons.

The last trick in 'default-project' is that it automatically generates the README for the new project. It might need some tweaking, but having proper documentation that details how to install and upgrade your project from the start is a major bonus. It is slightly biased towards our stack (mercural+bitbucket, sass) so feel free to fork our default-project and customize it to your liking!



Latest Tweets