Using multiple databases for a single Django project

Using different databases for each application in a Django project requires multiple changes to the project:

  1. A database configuration for each database within settings.py
  2. A reference to a database router for each application, set within settings.py
  3. The database router for each application, defined within each application’s directory

To start off, the DATABASES variable in settings.py is a dictionary of databases, each with a given name, and a set of properties defining the connection. In my experience, a 'default' database is required for Django to function.

Look at the following example:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'db1',
            'USER': 'djangouser',
            'PASSWORD': '******',
            'HOST': '127.0.0.1',
            'PORT': '5432',
            'OPTIONS': {
                'options': '-c search_path=django',
            }
        },
        'bar': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'db1',
            'USER': 'baruser',
            'PASSWORD': ''******',
            'HOST': '127.0.0.1',
            'PORT': '5432',
            'OPTIONS': {
                'options': '-c search_path=bar',
            }
        }}

Note that the different configurations do not necessary have to point to different databases, in this example, I’m referring to two different schemas (django and bar) of the same database, but with different credentials. This example satisfies the first of the three criteria.

In the same file, we’ll now define the database routers files which we’ll create in the third step. Django uses these routers to determine which of the databases defined in the first step to use for a given query. For our example, this configuration is as follows:

    DATABASE_ROUTERS =
       [
        'foo.FooRouter.FooRouter',
        'bar.BarRouter.BarRouter',
       ]

We’ll set these up in such a way that foo uses the FooRouter router, and bar uses the BarRouter router, which corresponds to the ‘django‘ and ‘bar‘ databases, respectively.

The third criterion is to set up the database router for each application, to indicate which database to use in each case (the database is indicated according to the name given in the configuration shown in the first step). Here’s an example of a database router:

class BarRouter:
    def db_for_read(self, model, **hints):
        if model._meta.model_name.startswith("bar"):
            return 'bar'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.model_name.startswith("bar"):
            return 'bar'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if obj1._state.db == 'bar' and \
                obj2._state.db == 'bar':
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None \
           **hints):
        if app_label == 'bar':
            return True
        return None

In each router you’ll need to define some characteristic of the model being queried to indicate whether that model belongs to the database for that application, in this simple example I’m checking whether the model (i.e. the database table) name starts with the ‘bar‘-prefix. This is a simple approach, you can define different characteristics for your own purposes.

It is also possible to use the application labels do determine the database. In the case of the default database you might want to accommodate the default authentication and session applications, among others. This snippet gives an example of such logic:

...
    route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'}

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'django'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'django'
        return None
...

Expanding to more than two databases just requires the addition of another database configuration, another router reference, and the router to go along with it. It is also possible to define a single router which incorporates all of the necessary logic, but I find it simpler to have them defined per-application.