My Django Development, Testing, and Production Environments
I've seen a lot of discussion lately about different strategies for developing and deploying Django applications. I wanted to share my current environment in the hopes that it might help someone new to Django or software development in general, and I was also hoping for some constructive criticism so that I can learn best practices. Most of my coding is done on my own, but I've been trying to stay away from cowboy coding habits as much as possible to keep my development practices disciplined.
I started out using Gedit with plugins and the Linux command shell, and that worked very well. As my projects grew larger, though, I really wanted a more feature-rich environment that would allow tighter integration of Subversion. I also did a small amount of development on a Windows machine (not by choice), and I wanted a development environment that I could use across multiple platforms. I settled on Eclipse after a lot of experimentation, and so far I've really enjoyed it.
My Development Environment
To start out with, I'll talk a little about my development environment. I use an Ubuntu laptop and desktop for development, switching back and forth depending on how I feel at the moment. The desktop is custom-built, and the laptop is from HP. They are both dual-core amd64 machines, and I've been very happy with how Linux support for the amd64 architecture has progressed.
I use the Eclipse Ganymede classic download with the Aptana, PyDev, and Subclipse plugins. The reason I use the classic download instead of the standalone Aptana Studio is twofold, actually. First, there's no amd64 version of the standalone Aptana download yet. Secondly, on the Windows machine I've noticed better compatibility with Eclipse classic for some of the other minor plugins I've tried out. I believe Aptana's standalone software is currently running Eclipse 3.2, and a few of the plugins I tried needed 3.3 or better.
For version control, I use Subversion. I host my own subversion server on my testing machine, which I'll talk more about in a moment. I organize my repository with the customary trunk
, branches
, and tags
folder in the root of the repository. Under branches
, I keep a separate testing branch and production branch for those environments. For each project in my repository, I have a directory underneath trunk
. In each project directory, I have a templates
and static
directory. When I want to test or deploy changes, I merge the revisions I want from trunk to one of my branches.
Under the home directory on my development machines, I have a full checkout from SVN including all the branches. Then, using symlinks I mirror the directory layout that my production host, WebFaction, uses. For this layout, I have a top-level webapps
directory. Underneath that I have a django
directory which I add to my PYTHONPATH, and a static
directory to house symlinks to each project's static content. I do this so that each site also has access to “shared” static content including common JS files, a reset stylesheet, etc. In my templates, “/static/project/” points to the project-specific files and “/static/shared/” points to the shared media.
In the django
directory, I symlink all of my project directories from my SVN checkout, including a project I call plugins
which includes svn:externals
links to several third-party apps. I also include Django itself as an external there.
When I develop, I always svn update
first to syncronize my checkout, and then I use the built-in Django development server. I have PostgreSQL installed on each development machine, and I use pg_dump dbname > dump.sql
from the production server and then psql dbname < dump.sql
on the development machine when I need a fresh copy of the database for an existing site.
My Testing Environment
For testing, I use a separate custom Ubuntu machine running a single-core 32-bit AMD processor. In my home directory I have just the testing branch checked out, and again I mirror my production environment through symlinks. The SVN repository is hosted on this machine, and I will soon be implementing periodic backups. I also host Trac from this machine for project management. To reach the machine remotely, I've set up DNS overrides with my production host. I'm planning on a cron script that will update the IP address periodically with WebFaction since my IP address changes every time I lose my connection to my ISP (mainly due to infrequent power outages).
I run PostgreSQL for the database and Apache for the webserver, using mod_python for interaction with Django. I have a VirtualHost set up for testing, and I add each site as a Location under that VirtualHost. That way each testing site sits under the testing domain as a root directory, such as http://testing.server.com/myproject/
and http://testing.server.com/myotherproject/
.
My Production Environment
For production, as I mentioned before, I use WebFaction. It allows Linux shell access through SSH, and they have a helpful library of knowledge-base entries and forum posts that have really made them easy to use. I have a checkout of just the production branch under my home directory, and then I use symlinks within their directory structure to set up the environment.
In the panel, I have a django
application installed and a static
application installed, which adds those directories under the webapps
directory. The django
directory also includes the Apache configuration files and a Python site-packages
directory. From the site-packages
directory, I've removed django
and use the svn:external
for Django from my plugins
project. I have each separate site set up as a VirtualHost entry in the Apache conf.
When I am ready to make changes in the production environment, I'll merge them to the production branch on my development machine and make sure there are no conflicts. My production branch already has the production settings (such as DEBUG = False) and my merges don't change that. Once I resolve any conflicts, I commit the production branch and then SSH into the production server to do an svn update
under the checkout directory. I run python2.5 manage.py syncdb
and whatever else I need to do in the command line under my project, and then restart the Apache server. From there, I fire up Firefox and check to see if everything worked.
So far this setup has served me well, but I'm very open to suggestions or questions. Please feel free to post comments to discuss.