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

Django ManyToMany a través de múltiples bases de datos

Existe una solución para Django 1.6+ (incluyendo 1.11) para MySQL y sqlite backends, por opción ForeignKey. db_restricción =Falso y Meta.db_table explícito . Si el nombre de la base de datos y el nombre de la tabla están entre comillas por ' ` ' (para MySQL) o por ' " ' (para otra base de datos), por ejemplo, db_table = '"db2"."table2"' ). Entonces no se cotiza más y el punto está fuera de cotización. Las consultas válidas son compiladas por Django ORM. Una mejor solución similar es db_table = 'db2"."table2' (que permite no solo uniones, sino que también está más cerca de cruzar la migración de restricciones de db)

db2_name = settings.DATABASES['db2']['NAME']

class Table1(models.Model):
    fk = models.ForeignKey('Table2', on_delete=models.DO_NOTHING, db_constraint=False)

class Table2(models.Model):
    name = models.CharField(max_length=10)
    ....
    class Meta:    
        db_table = '`%s`.`table2`' % db2_name  # for MySQL
        # db_table = '"db2"."table2"'          # for all other backends
        managed = False

Conjunto de consultas:

>>> qs = Table2.objects.all()
>>> str(qs.query)
'SELECT "DB2"."table2"."id" FROM DB2"."table2"'
>>> qs = Table1.objects.filter(fk__name='B')
>>> str(qs.query)
SELECT "app_table1"."id"
    FROM "app_table1"
    INNER JOIN "db2"."app_table2" ON ( "app_table1"."fk_id" = "db2"."app_table2"."id" )
    WHERE "db2"."app_table2"."b" = 'B'

Ese análisis de consultas es compatible con todos los backends de db en Django, sin embargo, otros pasos necesarios deben ser discutidos individualmente por backends. Estoy tratando de responder de manera más general porque encontré un pregunta importante similar .

La opción 'db_constraint' es necesaria para las migraciones, porque Django no puede crear la restricción de integridad de referencia
ADD foreign key table1(fk_id) REFERENCES db2.table2(id) ,
pero puede crearse manualmente para MySQL.

Una pregunta para backends particulares es si se puede conectar otra base de datos a la predeterminada en tiempo de ejecución y si se admite una clave externa de base de datos cruzada. Estos modelos también se pueden escribir. La base de datos conectada indirectamente debe usarse como una base de datos heredada con managed=False (porque solo una tabla django_migrations para las migraciones, el seguimiento se crea solo en la base de datos conectada directamente. Esta tabla debe describir solo tablas en la misma base de datos). Sin embargo, los índices para claves foráneas se pueden crear automáticamente en el lado administrado si el sistema de la base de datos admite dichos índices.

Sqlite3 :Tiene que adjuntarse a otra base de datos sqlite3 predeterminada en tiempo de ejecución (respuesta SQLite - ¿Cómo se unen tablas de diferentes bases de datos ), en el mejor de los casos por la señal connection_created :

from django.db.backends.signals import connection_created

def signal_handler(sender, connection, **kwargs):
    if connection.alias == 'default' and connection.vendor == 'sqlite':
        cur = connection.cursor()
        cur.execute("attach '%s' as db2" % db2_name)
        # cur.execute("PRAGMA foreign_keys = ON")  # optional

connection_created.connect(signal_handler)

Entonces, por supuesto, no necesita un enrutador de base de datos y un django...ForeignKey normal se puede usar con db_constraint=False. Una ventaja es que "db_table" no es necesario si los nombres de las tablas son únicos entre las bases de datos.

En MySQL claves externas entre diferentes bases de datos son fáciles. Todos los comandos como SELECCIONAR, INSERTAR, ELIMINAR admiten cualquier nombre de base de datos sin adjuntarlos previamente.

Esta pregunta era sobre bases de datos heredadas. Sin embargo, también tengo algunos resultados interesantes con las migraciones.