Python, Django with multiple databases

Asked

Viewed 999 times

3

To work with multiple databases in the Laravel framework, once configured all the connections in the configuration files, just define in the model which connection to the database that model will use, and then regardless of the operation the model will always point to a particular bank.

An example is when I have two clients each with their own bank, and I need to perform the CRUD in both banks, with Laravel I just define which model of which client to use and the operations are normally performed.

How do I do it using Django?

1 answer

3

You will be able to do this through Automatic Database Routing. It is a rather extensive setup, but let’s use a simple and practical example (taken from the documentation).

This one’s gonna have some Databases: one for app auth and all other apps will use a Primary/replica setting with two read-only replicates. This would be the setting for Databases:

DATABASES = {
    'default': {},
    'auth_db': {
        'NAME': 'auth_db',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'swordfish',
    },
    'primary': {
        'NAME': 'primary',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'spam',
    },
    'replica1': {
        'NAME': 'replica1',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'eggs',
    },
    'replica2': {
        'NAME': 'replica2',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'bacon',
    },
}

Now we will need to deal with the routing. First we will create a router that will know how to send queries from the app auth to the auth_db:

class AuthRouter:
    """
    Um router para controlar todas as operações de banco de
    dados dos models da app auth.
    """
    def db_for_read(self, model, **hints):
        """
        Definição do nome do banco para leitura.
        """
        if model._meta.app_label == 'auth':
            return 'auth_db'
        return None

    def db_for_write(self, model, **hints):
        """
        Definição do nome do banco para escrita.
        """
        if model._meta.app_label == 'auth':
            return 'auth_db'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Permite relações se um model na app auth está envolvido.
        """
        if obj1._meta.app_label == 'auth' or \
           obj2._meta.app_label == 'auth':
           return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Certifica que a app auth aparece apenas no banco 'auth_db'.
        database.
        """
        if app_label == 'auth':
            return db == 'auth_db'
        return None

And here we will create a router that will send everything from the other apps to our Primary/replica configuration, randomly choosing from which replica it will read:

import random

class PrimaryReplicaRouter:
    def db_for_read(self, model, **hints):
        """
        Leitura vai para um dos dois, aleatoriamente.
        """
        return random.choice(['replica1', 'replica2'])

    def db_for_write(self, model, **hints):
        """
        Escrita sempre no primary
        """
        return 'primary'

    def allow_relation(self, obj1, obj2, **hints):
        """
        Relações entre objetos são permitidas se ambos objetos
        estão no primary/replica.
        """
        db_list = ('primary', 'replica1', 'replica2')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None

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

At the end of all this, you will add the DATABASE_ROUTERS to your file settings.py of the project, with the path to the two previously created classes.

DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']

Has this other post which also addresses the same subject.

  • I noticed that it is possible to have only one router file, and in each method put a conditional pointing each app to a specific database. It’s a good practice?

  • It is possible, but for reasons of organization and maintainability, it is better to separate and have a file for each router, or at least a file but each router being a class.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.