Mysql
 sql >> Base de données >  >> RDS >> Mysql

Django ManyToMany avec plusieurs bases de données

Une solution existe pour Django 1.6+ (y compris 1.11) pour MySQL et sqlite backends, par option ForeignKey. db_constraint =Faux et explicite Meta.db_table . Si le nom de la base de données et le nom de la table sont entre guillemets par ' ` ' (pour MySQL) ou par ' " ' (pour une autre base de données), par exemple db_table = '"db2"."table2"' ). Alors il n'est plus entre guillemets et le point est hors guillemets. Les requêtes valides sont compilées par Django ORM. Une meilleure solution similaire est db_table = 'db2"."table2' (cela permet non seulement les jointures mais c'est aussi par un problème plus proche de la migration des contraintes croisées de la base de données)

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

Ensemble de requête :

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

Cette analyse de requête est prise en charge par tous les backends db dans Django, cependant, les autres étapes nécessaires doivent être discutées individuellement par les backends. J'essaie de répondre plus généralement parce que j'ai trouvé un question importante similaire .

L'option 'db_constraint' est nécessaire pour les migrations, car Django ne peut pas créer la contrainte d'intégrité de référence
ADD foreign key table1(fk_id) REFERENCES db2.table2(id) ,
mais il peut être créé manuellement pour MySQL.

Une question pour des backends particuliers est de savoir si une autre base de données peut être connectée à la valeur par défaut au moment de l'exécution et si une clé étrangère inter-base de données est prise en charge. Ces modèles sont également inscriptibles. La base de données indirectement connectée doit être utilisée comme une base de données héritée avec managed=False (car une seule table django_migrations pour les migrations, le suivi est créé uniquement dans la base de données directement connectée. Cette table ne doit décrire que les tables de la même base de données.) Les index pour les clés étrangères peuvent cependant être créés automatiquement du côté géré si le système de base de données prend en charge de tels index.

Sqlite3 :Il doit être attaché à une autre base de données sqlite3 par défaut au moment de l'exécution (réponse SQLite - Comment joindre des tables de différentes bases de données ), au mieux par le signal 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)

Ensuite, il n'a bien sûr pas besoin d'un routeur de base de données et d'un django...ForeignKey normal peut être utilisé avec db_constraint=False. Un avantage est que "db_table" n'est pas nécessaire si les noms de table sont uniques entre les bases de données.

Dans MySQL clés étrangères entre différentes bases de données sont faciles. Toutes les commandes telles que SELECT, INSERT, DELETE prennent en charge tous les noms de base de données sans les attacher au préalable.

Cette question portait sur les bases de données héritées. J'ai cependant des résultats intéressants aussi avec les migrations.