Managing multiple websites with a common database in Django - The 'Sites' Framework

(Comments)

Django provides the sites framework which allows us to associate the data (objects/models) and functionality with a particular website. This is particularly useful if you are managing multiple websites with a single code base (as in a content-management system) or multiple websites that share a common database.


To enable the sites framework,

  1. add the django.contrib.sites to your INSTALLED_APPS setting
  2. define the optional SITE_ID setting (this will be used by get_current_site() function if defined)
  3. run migrate


This framework mainly works based on the django.contrib.sites.models.Site model. The model has domain (stores fully qualified domain name) and name (stores a verbose/human-readable name) fields. The most obvious way of using the sites is to use the Site model as a foreign key in a model to associate the data with a website. For example, say, a post in a blog is associated with a website, then the model will be represented as:

from django.contrib.sites.models import Site

class BlogPost(models.Model):
    title = models.CharField(max_length=120)
    content = models.TextField()
    # .... some other fields
    site = models.ForeignKey(Site)

The list of BlogPosts of a site can be listed with:

post_list = BlogPost.objects.filter(site__domain='www.mydomain.com')

Of course, hard-coding the domain is not the proper way. The view has to get the list based on the site the visitor is viewing. The clean way of getting the current site as a Site object is to use the get_current_site method as follows:

from django.contrib.sites.shortcuts import get_current_site

def posts_view(request):
    current_site = get_current_site(request)
    post_list = BlogPost.objects.filter(site=current_site)
    # We have the needed posts

Django is smart. It also provides a get_current() method to the Site's object manager. So the statement current_site = get_current_site(request) can be replaced with current_site = Site.objects.get_current(), and this method sets the cache for current site, so the subsequent calls to Site.objects.get_current() will be served from cache.

CurrentSiteManager

If your data is tightly bound with sites and have the SITE_ID setting defined (different code base or settings per website), Django provides another enhancement - the CurrentSIteManager. This manger can be used along with the default model manager (django.db.models.Manger) as follows:

from django.contrib.sites.managers import CurrentSiteManager

class BlogPost(models.Model):
    title = models.CharField(max_length=120)
    content = models.TextField()
    # .... some other fields
    site = models.ForeignKey(Site)

    # objects = models.Manger() is implicit
    on_site = CurrentSiteManager()  # Your alternative manager

This helps us with filtering the objects based on the site. The query BlogPost.objects.filter(site=settings.SITE_ID) can be written as BlogPost.on_site.all(). The CurrentSiteManager will look for a foreign key field named site or a many to many field named sites to filter the data. If the field name is different from those, it can be specified in the initialization of the manager as:

from django.contrib.sites.managers import CurrentSiteManager

class BlogPost(models.Model):
    title = models.CharField(max_length=120)
    content = models.TextField()
    # .... some other fields
    published_in = models.ForeignKey(Site) # This is the field of interest

    # objects = models.Manger() is implicit
    on_site = CurrentSiteManager('published_in')

One other feature to note is the django.contrib.sites.middleware.CurrentSiteMiddleware middleware that sets the site attribute on the request object. Just add the django.contrib.sites.middleware.CurrentSiteMiddleware to the MIDDLEWARE (or MIDDLEWARE_CLASSES in old versions). The current site object will be available as request.site in Django's views.

Comments

Recent Posts

Archive

2022
2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom