June 6, 2013 -
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:
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
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:
+-- 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
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!