sql >> Base de Datos >  >> RDS >> Mysql

Aplicaciones Django de múltiples inquilinos:¿alterar la conexión de la base de datos por solicitud?

He hecho algo similar que está más cerca del punto 1, pero en lugar de usar middleware para establecer una conexión predeterminada, se usan enrutadores de base de datos Django. Esto permite que la lógica de la aplicación use varias bases de datos si es necesario para cada solicitud. Depende de la lógica de la aplicación elegir una base de datos adecuada para cada consulta, y esta es la gran desventaja de este enfoque.

Con esta configuración, todas las bases de datos se enumeran en settings.DATABASES , incluidas las bases de datos que pueden ser compartidas entre los clientes. Cada modelo que es específico del cliente se coloca en una aplicación Django que tiene una etiqueta de aplicación específica.

p.ej. La siguiente clase define un modelo que existe en todas las bases de datos de clientes.

class MyModel(Model):
    ....
    class Meta:
        app_label = 'customer_records'
        managed = False

Se coloca un enrutador de base de datos en settings.DATABASE_ROUTERS cadena para enrutar la solicitud de base de datos por app_label , algo como esto (no es un ejemplo completo):

class AppLabelRouter(object):
    def get_customer_db(self, model):
        # Route models belonging to 'myapp' to the 'shared_db' database, irrespective
        # of customer.
        if model._meta.app_label == 'myapp':
            return 'shared_db'
        if model._meta.app_label == 'customer_records':
            customer_db = thread_local_data.current_customer_db()
            if customer_db is not None:
                return customer_db

            raise Exception("No customer database selected")
        return None

    def db_for_read(self, model, **hints):
        return self.get_customer_db(model, **hints)

    def db_for_write(self, model, **hints):
        return self.get_customer_db(model, **hints)

La parte especial de este enrutador es el thread_local_data.current_customer_db() llamar. Antes de utilizar el enrutador, la persona que llama/la aplicación debe haber configurado la base de datos del cliente actual en thread_local_data . Se puede usar un administrador de contexto de Python para este propósito para insertar/abrir una base de datos de clientes actual.

Con todo esto configurado, el código de la aplicación se parece a esto, donde UseCustomerDatabase es un administrador de contexto para insertar/abrir un nombre de base de datos de clientes actual en thread_local_data para que thread_local_data.current_customer_db() devolverá el nombre correcto de la base de datos cuando el enrutador finalmente sea alcanzado:

class MyView(DetailView):
    def get_object(self):
        db_name = determine_customer_db_to_use(self.request) 
        with UseCustomerDatabase(db_name):
            return MyModel.object.get(pk=1)

Esta es una configuración bastante compleja ya. Funciona, pero intentaré resumir lo que veo como ventajas y desventajas:

Ventajas

  • La selección de la base de datos es flexible. Permite el uso de múltiples bases de datos en una sola consulta, tanto las bases de datos específicas del cliente como las compartidas se pueden usar en una solicitud.
  • La selección de la base de datos es explícita (no estoy seguro si esto es una ventaja o una desventaja). Si intenta ejecutar una consulta que llega a una base de datos de clientes pero la aplicación no ha seleccionado ninguna, se producirá una excepción que indica un error de programación.
  • El uso de un enrutador de base de datos permite que existan diferentes bases de datos en diferentes hosts, en lugar de depender de un USE db; declaración que supone que todas las bases de datos son accesibles a través de una única conexión.

Desventajas

  • Es complejo de configurar y hay bastantes capas involucradas para que funcione.
  • La necesidad y el uso de datos locales de subprocesos son oscuros.
  • Las vistas están llenas de código de selección de base de datos. Esto podría abstraerse usando vistas basadas en clases para elegir automáticamente una base de datos basada en parámetros de solicitud de la misma manera que el middleware elegiría una base de datos predeterminada.
  • El administrador de contexto para elegir una base de datos debe ajustarse alrededor de un conjunto de consultas de tal manera que el administrador de contexto aún esté activo cuando se evalúe la consulta.

Sugerencias

Si desea un acceso flexible a la base de datos, le sugiero que use los enrutadores de base de datos de Django. Use Middleware o una vista Mixin que configura automáticamente una base de datos predeterminada para usar para la conexión en función de los parámetros de solicitud. Es posible que deba recurrir a subprocesos de datos locales para almacenar la base de datos predeterminada que se usará de modo que cuando se active el enrutador, sepa a qué base de datos enrutar. Esto le permite a Django usar sus conexiones persistentes existentes a una base de datos (que puede residir en diferentes hosts si lo desea) y elige la base de datos para usar según el enrutamiento configurado en la solicitud.

Este enfoque también tiene la ventaja de que la base de datos para una consulta se puede anular si es necesario mediante el QuerySet using() función para seleccionar una base de datos que no sea la predeterminada.