Quickstart

First, make sure you have setup Django Dynamic DB Router by installing the package using pip install django-dynamic-db-router and make sure you have added DATABASE_ROUTERS=['dynamic_db_router.DynamicDbRouter'] to your Django settings.

Once you have installed and set up django-dynamic-db-router all your routing of queries between different databases can be done with dynamic_db_router.in_databases, which can be used as a context manager or function decorator. There are two main use cases for in_database. The first is to connect to databases that are already configured in your Django settings DATABASES parameter. The second is to establish a connection to a database that has not been configured, and route queries there. Both of these use cases will be described below.

Routing to Configured Databases

In the case that you have multiple databases set up in your django project, you will should have a settings.py file containing something like:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'my_local_database',
        'USER': 'postgres',
        'PASSWORD': 'my-pass',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    },
    'external': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'my_external_database',
        'USER': 'postgres',
        'PASSWORD': 'my-pass',
        'HOST': 'example.com',
        'PORT': '5432',
    },
    # ...
}
DATABASE_ROUTERS = ['dynamic_db_router.DynamicDbRouter']

possibly with additional databases configured. With this configuration, all queries will, by default, go to the 'default' database. To route queries to the 'external' database, you could use Django’s built in .using('external') method on querysets. However, this becomes cumbersome when doing a large number of queries, or using libraries that don’t take multiple databases into account.

It is much easier to do these queries in a context manager, where you can write the queries such that they will automatically be routed to the correct database. This is what the in_database context manager provides:

from dynamic_db_router import in_database

from my_app.models import MyModel
from some_app.utils import complex_query_function

with in_database('external'):
    input = MyModel.objects.filter(field_a="okay")
    output = complex_query_function(input)

In the example above, the MyModel.objects.filter query will be performed against the 'external' database, but so will any queries made by complex_query_function, which otherwise may be impossible to guarantee with .using. These queries will only be routed to the 'external' database for the with block, and will automatically route to the 'default' database outside of the with block again.

Often times when working with multiple databases, you want to control whether you can read and write to a given database. For this reason, the in_database context manager takes two optional parameters, read and write. These control whether reads and writes go to the provided database, respectively, and default to read=True and write=False.

Allowing writing to the 'external' database then, would look like:

with in_database('external', write=True):
    MyModel.objects.create(
        field_a='bad',
        field_b=17774,
    )

It should be noted that with write=False attempts to write inside the context manager will not fail, but will be routed to the 'default' database. This also holds for read=False. It is up to the user to understand what sorts queries are being run within the context manager. In the example above, if write were set to False, the MyModel object would still be created, but in the 'default' database.

Additionally, if you want to define a function which always pulls its results from a certain database, in_database can be used as a function decorator:

from dynamic_db_router import in_database

@in_database('external')
def get_external_models_count(models):
    counts = {}
    for model in models:
        counts[model] = model.objects.count()
    return counts

Whenever this function is run, it will route the queries in the function to the 'external' database. The decorator version of in_database takes all the same arguments as the context-manager version, so it is possible to control read/write permissions in the same way.

Dynamic Database Configuration and Routing

In addition to accessing databases that are already configured in django.conf.settings.DATABASES, django-dynamic-db-router can also be used to dynamically set up a database configuration, route queries to it and tear down the configuration as the context manager or decorated function exits.

In order for this to function properly, the database you are trying to connect to dynamically must already be set up with tables corresponding to whatever models you want to use to query them. Given such a database, dynamically connecting to it and querying it is as simple as passing in_database a dictionary with connection information, rather than a string:

from dynamic_db_router import in_database
from my_app.models import MyModel

external_db = {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': 'my_external_database',
    'USER': 'postgres',
    'PASSWORD': 'my-pass',
    'HOST': 'example.com',
    'PORT': '5432',
}

with in_database(external_db):
    target = MyModel.objects.get(field_b=17774)

In the example above, even though there is no entry for the database configuration in settings.DATABASES, in_databases is able to access the database, run the query, and clean up after itself.

When using a configuration as an argument, in_databases still supports read and write controls as described above, and supports use as a function decorator.