Wednesday, February 29, 2012

Django and Version Control for Multiple Environments/Developers

Ok so I recently started messing around with Django after getting my feet wet with a little python development. One thing that quickly became apparent was that if I was going to have a project that I write in django go live I would probably need a way to migrate code from one environment to another, or hopefully migrate my code to another developer to help with the development effort.

I searched around the web and most solutions I found had me keeping at least one file outside of my VCS (settings_local.py for instance). This was not a reasonable solution to me, as I am of the ilk that if I have a file in my project that gets edited, I want that file to have version control mechanisms in place :). I asked in the django irc chat and didn't really get the answer that I wanted but low and behold my good buddy Sean hooked me up from a linux chat I have been part of forever. Here is a quick tutorial/how to, to get the job done so you can keep your settings.py settings all in git/svn/nameVcsHere. I personally use git but that is not relevant to this discussion.

Step 1 - Create a settings_map

Create the settings_map.py file in the project folder (ie where your manage.py file is). The file is basically just a dictionary with key:value of the system -> settings. So for instance mine looks like this:

_settings_map = {
'BHM-LUS-V001':'settings_dev',
'BHM-LUS-V003':'settings_uat',
'BHM-LUS-V005':'settings_prod',
}

Using the first key:value for example 'BHM-LUS-V001' is the name of the server that I do my dev work on and I am going to put dev specific settings, like connection string info, in a file called settings_dev.py

Step 2 - Creating the various settings files

After I did this I made a copy of the settings.py file from inside my project folder and named it settings_common.py. This file will contain all the information that will remain the same no matter where I develop. Things like language_code, static_url, staticfiles_finders etc. I removed (read commented out for now) the sections I was going to extrapolate to the environment settings files.

Next I copied the settings.py file again but this time called it settings_dev.py. This file is FAR more sparse than the other file I copied. I basically removed EVERYTHING in this file except the database settings, ADMINS section, Debug info, etc. So it ended up looking something like this:

from settings_common import *

# Django settings for Dev Environment project.

DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
('Paul J Warner', 'email@adddress.com'),
)

MANAGERS = ADMINS

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_name',
'USER': 'dbuser',
'PASSWORD': 'dbpass',
'HOST': '',
'PORT': '',
}
}

A KEY thing to note here is the VERY first line, from settings_common import *. This pulls in all the common settings and allows us to just deal with the settings that will change from environment to environment. This is also handy because as requirements change (ie multiple developers working in multiple timezones for instance) you can extrapolate from the settings common into the settings_env.

Step 3 - modify manage.py to use the new settings

manage.py is a pretty simple file, if you open it up, that does a TON of stuff. But that is outside the scope of my post. We are just going to modify it so that it can use our new settings files.

First you need to import our system_mappings dictionary
from settings_map import _settings_map

Next modify the DJANGO_SERTTINGS_MODULE sections to look like this:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", _settings_map[os.uname()[1]])

What this is doing is telling the django system to look at your settings map dictionary and use the VALUE from the one that matches the hostname of the system you are working on. This is the part to me that was just brilliant, and while Sean calls it a hack I think its a really elegant way of getting the job done. Now as long as the hostname of the computer that I am working on is called BHM-LUS-V001 django will use my settings_dev settings. When I add additional machines like BHM-LUS-V003 all I will need to do is create the settings_env file and map it to that in the settings_map file.

The benefits of all this work (after you do it its actually not that bad in retrospect) is that you can now keep all your settings in version control. This is pretty great when you are working with other developers as well so that they can easily create their own from yours as a template. After I got this all set up I moved into my UAT environment with very little issue.

If you stumble upon this post and have questions or comments (maybe I missed a step) let me know.

1 comment:

  1. I took your ideas and cleaned them up a bit to remove the need to modify wsgi configuration or manage.py

    https://github.com/docgnome/django-settings-template

    ReplyDelete