Mysql
 sql >> Database >  >> RDS >> Mysql

Django ManyToMany attraverso più database

Esiste una soluzione per Django 1.6+ (incluso 1.11) per MySQL e sqlite backend, tramite l'opzione ForeignKey. db_vincolo =Falso ed esplicito Meta.db_table . Se il nome del database e il nome della tabella sono virgolette da ' ` ' (per MySQL) o da ' " ' (per altri db), ad esempio db_table = '"db2"."table2"' ). Quindi non è più quotato e il punto è fuori quotato. Le query valide sono compilate da Django ORM. Una soluzione simile migliore è db_table = 'db2"."table2' (che consente non solo i join ma è anche di un problema più vicino alla migrazione di vincoli db incrociati)

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

Serie di query:

>>> 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'

L'analisi della query è supportata da tutti i backend db in Django, tuttavia, altri passaggi necessari devono essere discussi individualmente dai backend. Sto cercando di rispondere in modo più generale perché ho trovato un domanda importante simile .

L'opzione 'db_constraint' è necessaria per le migrazioni, perché Django non può creare il vincolo di integrità di riferimento
ADD foreign key table1(fk_id) REFERENCES db2.table2(id) ,
ma può essere creato manualmente per MySQL.

Una domanda per particolari back-end è se un altro database può essere connesso all'impostazione predefinita in fase di esecuzione e se è supportata una chiave esterna tra database. Questi modelli sono anche scrivibili. Il database connesso indirettamente deve essere utilizzato come database legacy con managed=False (perché solo una tabella django_migrations per le migrazioni il tracciamento viene creato solo nel database direttamente connesso. Questa tabella dovrebbe descrivere solo le tabelle nello stesso database.) Gli indici per chiavi esterne possono tuttavia essere creati automaticamente sul lato gestito se il sistema di database supporta tali indici.

Sqlite3 :deve essere collegato a un altro database sqlite3 predefinito in fase di esecuzione (risposta SQLite - Come si uniscono tabelle di database diversi ), nella migliore delle ipotesi dal segnale 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)

Quindi non ha bisogno di un router di database ovviamente e di un normale django...ForeignKey può essere utilizzato con db_constraint=False. Un vantaggio è che "db_table" non è necessario se i nomi delle tabelle sono univoci tra i database.

In MySQL chiavi esterne tra database diversi sono facili. Tutti i comandi come SELECT, INSERT, DELETE supportano qualsiasi nome di database senza allegarli in precedenza.

Questa domanda riguardava i database legacy. Ho comunque dei risultati interessanti anche con le migrazioni.