The Power of Django: 9 websites, 1 database

Django Logo

# Shoutouts
Most of this information came from Franchising: Running Multiple Sites from one Django Codebase by Huy Nguyen.  Really great article.

# Problem Context
For this project, we had to build 9 splash pages for a QR Code campaign. Each website had to have its own branding, and we were afforded the luxury of having a separate domain name for each page. By leveraging Django, we were able to provide a single page CMS to manage all of the splash pages while each splash page exists on its own domain.

# Solution
At some point from the initial request to the served template we had to distinguish which domain the request was generated from. This was accomplished at the apache level, using the django.wsgi script to add an environment variable equal to the domain the request originated from. The different django.wsgi scripts (1 for each domain) were then called by each virtual host individually.

At this point you should have a django project already created. There will be a single project that all of the individual sites are served from. Create the main project as you would at the beginning of any normal project.

# Setup Apache to call individual wsgi files
In your virtual host configuration file, you want to declare an alias that points to a common directory that will be used to hold the media for all the different domains, then point each domain at its own django.wsgi script:

Alias /media/ /directory/to/SharedDomainForAllSites.com/htdocs/media/
WSGIScriptAlias / /path/to/django_project/apache/IndividualSiteName/django.wsgi

Now in /path/to/django_project/apache/ create a directory for each domain that will call your project. We will put the django.wsgi file from above into this directory.

$ cd /path/to/django_project/
$ mkdir apache
$ cd apache
$ mkdir IndividualSiteName
$ cd IndividualSiteName
$ vim django.wsgi

Inside each django.wsgi file, set the OVERLOAD_SITE variable to a name that identifies each domain uniquely. We will use this variable to tell django which CSS file to load, which template to use, which domain to search for media files, etc.

import os
import os.path
import sys

sys.path.append('/path/to/django/django_project')

os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project.settings'
os.environ['OVERLOAD_SITE'] = 'IndividualSiteName'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Back up in your main project directory, were going to modify the settings.py file to overload the MEDIA_ROOT and MEDIA_URL variables based on the request from each domain. At the bottom of settings.py, add the function that will overload the variables:

#
# IMPORT CUSTOM SETTINGS BASED ON DOMAIN MAKING THE CALL
#
import os
OVERLOAD_SITE = os.environ.get('OVERLOAD_SITE')
if OVERLOAD_SITE:
        OVERLOAD_SITE_MODULE ="site_overloads" + "." + OVERLOAD_SITE
        exec "from %s.settings import *" % (OVERLOAD_SITE_MODULE)
        # Overload the MEDIA_ROOT with site specific path
        try:
                MEDIA_ROOT = OVERLOAD_MEDIA_ROOT
        except:
                pass
        # Overload the MEDIA_URL with site specific URL
        try:
                MEDIA_URL = OVERLOAD_MEDIA_URL
        except:
                pass

For the MEDIA_ROOT and MEDIA_URL configuration at the top of the settings.py file, set a directory that will be a common directory used to host the media for all domains. If you override the MEDIA_URL for every domain, these settings will be ignored, so their values arn’t that important.

To set the override values, create the site_overloads directory that is referenced in the function we just added to settings.py. It should be in the project root.  Inside site_overloads, create a directory for each individual domain that has the same name as the OVERLOAD_SITE variable from the django.wsgi file. Inside of site overloads, you must add a blank __init__.py file, or django will not be able to pull files from the directory:

$ cd /path/to/django_project/
$ mkdir site_overloads
$ cd site_overloads
$ touch __init__.py
$ mkdir IndividualSiteName
$ cd IndividualSiteName
$ touch __init__.py

Now we will add a settings.py and urls.py file to each folder that will provide the overloads for each specific domain:

# settings.py
# Overload Default MEDIA_ROOT
# Use this if you want to serve up separate media for each domain
#OVERLOAD_MEDIA_ROOT = '/c2g/sites/auburnparkave.com/htdocs/media/'

# Overload Default MEDIA_URL
OVERLOAD_MEDIA_URL = 'http://IndividualSiteName.com/media/'

In your Apache Virtualhost config, recall that we set an Alias to /media/.  This Alias will allow you to put any domain in OVERLOAD_MEDIA_URL and have the single media directory exist at http://EachDomainName.com/media/.

New lets look at the urls.py.  For our project, each domain was hosting a single splash page, so we were able to serve all requests by providing the context straight from the url, letting us completely skip making any views.

# urls.py
from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
from pages.models import Page

urlpatterns = patterns('',
(r'^$', direct_to_template, {'template': 'SingleTemplate.html',
'extra_context': {'page': Page.objects.get(name__exact='IndividualSiteName'),
'sliderImages': Page.objects.get(name__exact='IndividualSiteName').slider.photos.all()}}),
)

The above assumes  you have a Page class in your models.py with a name field and a slider field.  You can modify the code to request anything you want from your models, or call a view for more indepth interaction.

Well, that should get you separate domains all hooked up to a single django app, capable of having a shared or individual media directories as well as individual domain names, all with a single page CMS for managing all your sites.

Enjoy.

This entry was posted in Django. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *