Imagine we are running a successful online store for cat food (let’s call it catfood247.com). Since things are going so well, we would like to expand our business with a second store for dog food, dogfood247.com. Does this mean we’ll have to set up a separate server even though the two stores will be very similar and share a lot of code? Having more servers means higher maintenance & running costs which are obviously things that, if possible, we would like to avoid.
Luckily, Django’s built-in “sites” framework enables us to run two or more websites within a single Django installation. Consider the following project layout:
.
└── petfood
├── petfood
│ ├── settings.py
├── catfood
│ ├── urls.py
│ ├── views.py
│ ├── …
├── dogfood
│ ├── urls.py
│ ├── views.py
│ ├── …
├── manage.py
└── …
We have three Django apps: petfood
, the main app holding the global settings.py
file every Django project needs to have; and catfood
as well as dogfood
, the two apps representing the actual sites to be served.
In addition to the app directories, we need to create two instances of Django’s Site
model. The Site
model is a simple Django model meant to logically represent a website by its domain & user-defined name. In our case, the following two Site
objects are needed:
Site(name='catfood', domain='catfood247.com')
Site(name='dogfood', domain='dogfood247.com')
In other words, each site is represented by a Django app directory (including it’s own URLconf) and a corresponding Site
object in Django’s database.
The only thing left to do is create a mechanism for determining the right URLconf on a per-request basis. Django’s documentation gives us a valuable hint at how to achieve what we want:
When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute:
1. Django determines the root URLconf module to use. Ordinarily, this is the value of the
– How Django processes a requestROOT_URLCONF
setting, but if the incomingHttpRequest
object has aurlconf
attribute (set by middleware), its value will be used in place of theROOT_URLCONF
setting.
2. (…)
In other words, Django makes it possible to set a URLconf for each separate request, thereby allowing us to differentiate between to or more Site
s and their respective URLconf. Let’s go ahead and define a new middleware that sets the request.urlconf
attribute based on the requested site’s name (e.g. catfood
):
class SetURLConfMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.urlconf = f"{request.site.name}.urls"
response = self.get_response(request)
return response
Don’t forget to add this middleware to the MIDDLEWARE
list in your settings.py
in order to activate it. Also, make sure to insert it after Django’s CurrentSiteMiddleware
so that it has access to the request.site
attribute.
And that’s all there is to it! From now on, whenever Django receives a request, it first determines the site the request should be forwarded to based on the request’s domain. This simple method makes it possible to support an arbitray number of sites within a single Django setup.
Note for Heroku users: Don’t forget to register each of your domains with your Heroku app!