Dec 09

Django Sites framework and a multi-site CMS

Serving multiple Django sites from a single application instance

The Django Sites framework has been widely adopted by many Django apps, as a way to use a single Django installation and a shared database, to serve multiple sites, while keeping the various model objects separated. This greatly simplifies the job of managing multiple Django applications, and makes it possible, for instance, to serve an article up on multiple websites, each running its own separate set of applications, some or all of which may be shared.

The framework is fairly flexible. As the documentation demonstrates, one may use it to associate content with sites in a OneToMany or ManyToMany relationship. For example, you may decide that articles always belong to a single site, or perhaps that they can be shared between multiple sites.

The sites framework is used by a number of components, including the comments and the syndication systems. In this way, comments or feeds are associated with a specific site, and objects can easily be filtered by sites, using the CurrentSiteManger provided by the Sites framework.

The Sites framework makes an assumption: Every site has its own settings.py, and the SITE_ID for a site is specified in settings.py. This fact is used by the sites framework to determine the current site. Thus, when one runs the code,

current_site = Site.objects.get_current()

the site framework checks the current SITE_ID in django.conf.

This is all very reasonable. However, there is one huge assumption being made here, namely, that one never wants to run multiple sites on the same application, with the samesettings.py file. Because the sites framework forces you to set a SITE_ID in settings.py, it is in not possible to serve content for multiple domains from a single application.

Django is not designed to be domain-neutral. It does not treat the domain name of your site as an arbitrary parameter, and allow you to do with it as you wish. Consider how urls.py is setup. The domain name is stripped from the URL before your URL configurations can touch it. You cannot grab the URL as a parameter to be passed to a view. Why? Because the URL belongs to the site. All of the apps assume that domain = new site = new settings.py. This has some consequences that are difficult to work around.

Consider the following scenario: You are designing a CMS to manage multiple websites from a single application on a single server. If we do this the way the Sites framework wants us to do it, we need to create a separate Django instance for each website. Thus, we multiply our initial memory usage by as many sites as we wish to serve. Setting up a site now requires us to not only setup a new wsgi or mod_python instance (assuming Apache), but also to setup a separate settings.py file (and possibly a separate urls.py file) for every site.

What is the alternative? How about having a single Django instance, whether using fast_cgi, wsgi, or mod_python, serves all the sites. One instance to rule them all. This app stores templates, page content, and stylesheets in the database. All that is necessary to create a new site, is point a domain at your Django CMS server, and a new domain object, setup templates and stylesheets from within admin, and start adding content.

However, to do this, we must scrap the Django sites framework, and implement our own. That's fine, and to be expected, since the sites framework serves another purpose. No use using the wrong tool for the job, right? Well, there is this niggling little problem, namely that all the Django apps that might be used for multiple sites also assume you will be using separate Django configurations for each site.

Take my example of the single-instance to rule them all. I want to add the Comments framework. I want to use it on multiple blog or news sites, each with its own look and feel. Therefore, I need to be able to customize the preview and post templates for every website. If I am doing my sites the Django way — no problem, because every app is running in a separate instance, each with its own template path. But how do I do it with a single instance? I can't. Whatever templates I choose to use with the comments framework, end up being used for every website. My only alternative is either to roll my own comment system, or to spin off django.contrib.comments into its own app, and develop it on my own.

I cannot really complain. When you don't run with the current, you cannot expect everything to just work. But I have to wonder if there might not be some benefit to re-thinking the way in which sites, settings.py and urls.py interact. But, as they say, I Want A Pony. In particular — will it ever be possible to treat the domain as just another urls.py component, without losing the ability to run separate domains in separate instances? This would have huge benefits. Imagine using a catch-all DNS record, and being able to specify a URL config this way:

(r'(?P<prefix>[-\w]+)\.(?P<domain>.[-\w]+)/page/(?P<slug>[-\w]+)/*', 'myapp.views.getpage')

What do you think?

About

I'm a Lutheran pastor, a CTO, a father, amateur photographer, programmer, Irish music fan, and all around geek, but I only have one blog. So, you will find here a mix of theology, photography, geek speak, family news, and whatever else strikes my fancy. If you get confused, there are now categories…

Subscribe

Categories

Elsewhere

Recent Posts

Archive

BlogRoll