Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Django ManyToMany com vários bancos de dados


Existe uma solução para o Django 1.6+ (incluindo 1.11) para MySQL e sqlite back-ends, pela opção ForeignKey. db_constraint =Falso e Meta.db_table explícito . Se o nome do banco de dados e o nome da tabela estiverem entre aspas por ' ` ' (para MySQL) ou por ' " ' (para outro db), por exemplo db_table = '"db2"."table2"' ). Então não é mais citado e o ponto está fora de citação. As consultas válidas são compiladas pelo Django ORM. Uma solução semelhante melhor é db_table = 'db2"."table2' (que permite não apenas junções, mas também é por um problema mais próximo da migração de restrição cruzada de banco de dados)
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'

Essa análise de consulta é compatível com todos os back-ends de banco de dados no Django, porém outras etapas necessárias devem ser discutidas individualmente pelos backends. Estou tentando responder de forma mais geral porque encontrei um pergunta importante semelhante .

A opção 'db_constraint' é necessária para migrações, pois o Django não pode criar a restrição de integridade de referência
ADD foreign key table1(fk_id) REFERENCES db2.table2(id) ,
mas pode ser criado manualmente para MySQL.

Uma questão para back-ends específicos é se outro banco de dados pode ser conectado ao padrão em tempo de execução e se uma chave estrangeira de banco de dados cruzado é suportada. Esses modelos também são graváveis. O banco de dados conectado indiretamente deve ser usado como um banco de dados legado com managed=False (porque apenas uma tabela django_migrations para rastreamento de migrações é criado apenas no banco de dados conectado diretamente. Essa tabela deve descrever apenas tabelas no mesmo banco de dados.) Índices para chaves estrangeiras podem, entretanto, ser criados automaticamente no lado gerenciado se o sistema de banco de dados suportar tais índices.

Sqlite3 :Ele deve ser anexado a outro banco de dados sqlite3 padrão em tempo de execução (answer SQLite - Como você junta tabelas de diferentes bancos de dados ), na melhor das hipóteses pelo sinal 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)

Então ele não precisa de um roteador de banco de dados, é claro, e um django...ForeignKey normal pode ser usado com db_constraint=False. Uma vantagem é que "db_table" não é necessário se os nomes das tabelas forem exclusivos entre os bancos de dados.

Em MySQL chaves estrangeiras entre bancos de dados diferentes são fáceis. Todos os comandos como SELECT, INSERT, DELETE suportam qualquer nome de banco de dados sem anexá-los previamente.

Esta pergunta era sobre bancos de dados legados. No entanto, tenho alguns resultados interessantes também com migrações.