Una de las características que contiene Django es permitir trabajar con MÚLTIPLES BASES DE DATOS, por ello, en este post te voy a explicar como puedes configurar tu proyecto para activar esta configuración

Definiendo nuestras Bases de Datos

La configuración de Base de Datos en todo proyecto de Django se encuentra en el archivo settings.py, en el apartado

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
}

Para empezar, debemos entender en que la configuración está dentro de un DICCIONARIO, por ende tendremos clave - valor, la clave será llamada ALIAS, este alias es el que desde ahora nos servirá para identificar la Base de Datos a la cual haremos referencia, por defecto, ya viene configurada con 1 Base de Datos llamada default.

Django internamente utiliza algo llamado Routers de Bases de Datos, aquí tiene definido una clase de tipo Router que lo que contiene es la configuración necesaria para aplicar migraciones en la base de datos que se requiera, por defecto, el Router default apuntará todas las migraciones de nuestras aplicaciones y de las aplicaciones predeterminadas(admin, sessions, contentypes, auth)

Un ejemplo de un Router es:

class DefaultRouter:
    """
    A router to control all database operations on models in the
    all applications.
    """

    def db_for_read(self, model, **hints):
        return 'default

    def db_for_write(self, model, **hints):
        return 'default

    def allow_relation(self, obj1, obj2, **hints):
        return True

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

Como puedes apreciar, existen 4 métodos definidos dentro del router DefaultRouter, los cuales son:

def db_for_read(model, **hints)

Devuelve la base de datos que debe usarse para operaciones de lectura para objetos de tipo Model.

Si una operación de base de datos puede proporcionar información adicional que pueda ayudar a seleccionar una base de datos, se proporcionará en el diccionario de **hints.

Devuelve None si no hay ninguna sugerencia.

def db_for_write(model, **hints)

Devuelve la base de datos que se debe utilizar para las escrituras de objetos de tipo Model.

Si una operación de base de datos puede proporcionar información adicional que pueda ayudar a seleccionar una base de datos, se proporcionará en el diccionario de **hints.

Devuelve None si no hay ninguna sugerencia.

def allow_relation(obj1, obj2, **hints)

Devuelve True si se debe permitir una relación entre obj1 y obj2, False si la relación debe evitarse o None si el enrutador no tiene opinión. Esta es puramente una operación de validación, utilizada por ForeignKey y ManyToMany para determinar si se debe permitir una relación entre dos objetos.

Si ningún enrutador tiene una opinión (es decir, todos los enrutadores devuelven None), solo se permiten las relaciones dentro de la misma base de datos.

def allow_migrate(db, app_label, model_name=None, **hints)

Determine si la operación de migración puede ejecutarse en la base de datos con el alias db. Devuelve True si la operación debe ejecutarse, False si no debe ejecutarse o None si el enrutador no tiene opinión.

El argumento posicional app_label es la etiqueta de la aplicación que se está migrando.

model_name lo establecen la mayoría de las operaciones de migración en el valor de model._meta.model_name (la versión en minúsculas del modelo __name__) del modelo que se está migrando. Su valor es None para las operaciones RunPython y RunSQL a menos que lo proporcionen mediante sugerencias.

Ciertas operaciones utilizan **hints para comunicar información adicional al enrutador.

Cuando se establece model_name, los **hints normalmente contienen la clase de modelo bajo la clave 'model'. Tenga en cuenta que puede ser un modelo history y, por lo tanto, no tener atributos, métodos o administradores personalizados. Solo debes confiar en _meta.

Este método también se puede utilizar para determinar la disponibilidad de un modelo en una base de datos determinada.

makemigrations siempre crea migraciones para cambios de modelo, pero si allow_migrate () devuelve False, cualquier operación de migración para model_name se omitirá silenciosamente al ejecutar migrate en la base de datos. Cambiar el comportamiento de allow_migrate () para modelos que ya tienen migraciones puede resultar en claves foráneas rotas, tablas adicionales o tablas faltantes. Cuando makemigrations verifica el historial de migración, omite las bases de datos donde no se permite migrar ninguna aplicación.

Un enrutador no tiene que proporcionar todos estos métodos; puede omitir uno o más de ellos. Si se omite uno de los métodos, Django omitirá ese enrutador cuando realice la verificación correspondiente.

Hints

Los hints recibidas por el enrutador de la base de datos se pueden utilizar para decidir qué base de datos debe recibir una solicitud determinada.

En la actualidad, la única pista que se proporcionará es una instancia, una instancia de objeto que está relacionada con la operación de lectura o escritura en curso. Esta podría ser la instancia que se está guardando o puede ser una instancia que se agrega en una relación de muchos a muchos. En algunos casos, no se proporcionará ninguna pista de instancia. El enrutador verifica la existencia de una sugerencia de instancia y determina si esa sugerencia debe usarse para alterar el comportamiento del enrutamiento.

Ejemplo

Conociendo esto, podemos determinar que para poder utilizar Multiples Bases de Datos, primero necesitas agregar la configuración a nuestro settings.py con las bases de datos que deseas utilizar, cabe resaltar que puedes utilizar la combinación de Bases de Datos que desees, no es necesario que todas sean iguales(cabe indicar que puedes utilizar cualquier base de datos con la que tenga compatibilidad Django).

Para nuestro ejemplo crearemos 2 bases de datos:

  • users_db: para las aplicaciones predeterminadas de Django y será utilizando el gestor de SQLITE
  • products_db: para la aplicación products que contiene modelos propios de la lógica de nuestro sistema a desarrollar y será utilizando el gestor POSTGRESQL.

DATABASES = {
    'default': {},
    'users_db':{
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'users',
    },
    'products_db':{
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'products',
        'PASSWORD': 'developer',
        'USER': 'oliver2',
        'PORT': 5432,
        'HOST': 'localhost'
    }
}

Debemos saber que el alias default lo dejaremos vacío puesto que no lo utilizaremos.

Luego de haber agregado esto sobreescribiremos el LISTADO DE ROUTERS PARA LAS BASES DE DATOS de nuestro proyecto, esto lo haremos colocando la siguiente línea de código en tu archivo settings.py

DATABASE_ROUTERS = [
    'database_routers.auth_router.AuthRouter',
    'database_routers.product_router.ProductRouter'
]

Cada elemento de este atributo, es la ruta donde estará cada router que crearemos, para nuestro ejemplo hemos creado 2 routers, uno para la base de datos users_db y otro para products_db, los cuales están a continuación.

class AuthRouter:

    router_app_labels = {'auth', 'contenttypes', 'sessions', 'admin'}

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

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.router_app_labels:
            return 'users_db'
        return None
    
    def allow_relation(self, obj1, obj2, **hints):
        if(
            obj1._meta.app_label in self.router_app_labels or
            obj2._meta.app_label in self.router_app_labels
        ):
            return True
        return None
    
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.router_app_labels:
            return db == 'users_db'
        return None
class ProductRouter:

    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'products':
            return 'products_db'
        return None
    
    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'products':
            return 'products_db'
        return None
    
    def allow_relation(self, obj1, obj2, **hints):
        if (
            obj1._meta.app_label == 'products' or 
            obj2._meta.app_label == 'products'
        ):
            return True
        return None
    
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label == 'products':
            return db == 'products_db'
        return None

Una vez colocado esto, ya tendríamos configurado nuestro proyecto para trabajar con varias bases de datos, no debemos realizar nada más, lo que toca ahora es ejecutar las migraciones, para ello hay que tener en cuenta lo siguiente:

  • makemigrations: se ejecuta de forma normal cada vez que hemos realizado cambios en algún o algunos modelos.
  • migrate: este comando es especial, cuando ejecutamos el comando python manage.py migrate, ejecuta la configuración de Base de Datos que se ha colocado en el ALIAS DEFAULT. Para poder ejecutar las migraciones en nuestras diferentes bases de datos, debemos tener un orden, debemos ejecutar el comando migrate indicando la base de datos a la cual debe aplicar las migraciones.

Por ejemplo, en nuestro caso, el alias DEFAULT no se utilizará, por ello no debemos ejecutar el comando a secas python manage.py migrate, sino debemos ejecutar el comando indicando cada base de datos que tenemos o donde sabemos que deben aplicarse las migraciones.

Primero para users_db por estar colocado su Router primero y por que tiene migraciones, el comando quedaría: python manage.py migrate --database=users_db

Y luego para products_db: python manage.py migrate --database=products_db

Igual te dejo el enlace a nuestro tutorial en nuestro canal de Youtube para una explicación aún mas detallada.

Otros Posts


Convertir Instancia de un Modelo de Django a un Diccionario
12 de Febrero de 2022 • Oliver Sandoval
Diferentes formas de convertir una instancia de un Modelo de Django a un Diccionario
Leer mas »
Configurar Multiples Bases de Datos con Django
21 de Octubre de 2021 • Oliver Sandoval
Multiples Bases de Datos con Django
Leer mas »
Autenticación en Django Rest Framework
4 de Septiembre de 2021 • Oliver Sandoval
Autenticacion en Django Rest Framework
Leer mas »