Maintaining a Database-per-branch for Django Development
When you are doing development on multiple branches in parallel, a simple git checkout
will update your code to match any branch you like. But unfortunately, if you’re connecting
to a database whose schema is different on each branch, there’s a disconnect that causes
a lot of problems.
When doing Django development, to solve this issue I maintain a unique database for each branch, and set up a git hook to make sure that I’m pointing to the right database after each branch switch.
A Detailed Approach
In my setup, settings.py
imports an optional local_settings.py
that is not checked into git.
The git hook script below will alter the BRANCH
setting in that file to match the branch you just switched to,
if the branch is master, staging, or dev. If not, it leaves it alone. (The idea here is that
you don’t want to bother setting up a whole database for every throwaway branch, but you could - just
remove the if
from the script.)
post-checkout git hook
Place this script code in your .git/hooks/post-checkout
file to set the BRANCH variable:
#!/bin/sh
# if this is a file checkout – do nothing
if [ "$3" == "0" ]; then exit; fi
newHEAD=$2
newHEADName=`git symbolic-ref --short HEAD`
if [[ "$newHEADName" =~ ^(master|staging|dev)$ ]]; then
echo [hooks/post-checkout] Changing BRANCH to $newHEADName: sed -i "s/^BRANCH = .*/BRANCH = '$newHEADName'/" myapp/local_settings.py
sed -i "s/^BRANCH = .*/BRANCH = '$newHEADName'/" ventus/local_settings.py
else
echo [hooks/post-checkout] Did not change DB setting, unknown $newHEADName.
fi
settings.py imports local_settings
In the settings.py
, we load in local_settings.py
like so:
DATABASES = {...}
## Read local settings. `get_additional_local_settings` lets you add settings
## in your local_settings that are a function of other settings. I pass ENV, here,
## but if you need other variables, simply add them. Make sure you accept
## **kwargs in your `get_additional_local_settings` in case others change the list.
def get_additional_local_settings(**kwargs):
return {}
try:
from .local_settings import *
except ImportError:
pass
additional_local_settings = get_additional_local_settings(
BRANCH=BRANCH,
DATABASES=DATABASES
)
if additional_local_settings:
for setting_name, setting_value in additional_local_settings.items():
globals()[setting_name] = setting_value
local_settings.py alters database using BRANCH
Then, the local_settings.py
should include this:
def get_additional_local_settings(BRANCH, DATABASES, **kwargs):
db_name = 'mydbname_{}'.format(BRANCH, 'local')
for k, obj in DATABASES.items():
obj['NAME'] = db_name
return {
'DATABASES': DATABASES,
}
# BRANCH will be updated by post-checkout git hook.
BRANCH = 'master'