数据库
首页 > 数据库> > python – Django ManyToMany通过多个数据库

python – Django ManyToMany通过多个数据库

作者:互联网

TLTR:Django在SQL查询中不包含数据库名称,我可以以某种方式强制它执行此操作还是有解决方法?

长版:

我有两个遗留的MySQL数据库(注意:我对数据库布局没有影响),我正在使用Django 1.11和python 3.6上的DRF创建一个只读API

我正在使用此处建议的SpanningForeignKey字段来解决MyISAM DB的参照完整性限制:https://stackoverflow.com/a/32078727/7933618

我正在尝试通过MultiToMany通过DB1上的表将DB1中的表连接到DB2中的表.这是Django正在创建的查询:

SELECT "table_b"."id" FROM "table_b" INNER JOIN "throughtable" ON ("table_b"."id" = "throughtable"."b_id") WHERE "throughtable"."b_id" = 12345

这当然给了我一个错误“表’DB2.throughtable’不存在”因为ontable在DB1上,我不知道如何强制Django用数据库名称为表添加前缀.查询应该是:

SELECT table_b.id FROM DB2.table_b INNER JOIN DB1.throughtable ON (table_b.id = throughtable.b_id) WHERE throughtable.b_id = 12345

app1 db1_app / models.py的模型:(DB1)

class TableA(models.Model):
    id = models.AutoField(primary_key=True)
    # some other fields
    relations = models.ManyToManyField(TableB, through='Throughtable')

class Throughtable(models.Model):
    id = models.AutoField(primary_key=True)
    a_id = models.ForeignKey(TableA, to_field='id')
    b_id = SpanningForeignKey(TableB, db_constraint=False, to_field='id')

app2 db2_app / models.py的模型:(DB2)

class TableB(models.Model):
    id = models.AutoField(primary_key=True)
    # some other fields

数据库路由:

def db_for_read(self, model, **hints):
    if model._meta.app_label == 'db1_app':
        return 'DB1'

    if model._meta.app_label == 'db2_app':
        return 'DB2'

    return None

我可以强制Django在查询中包含数据库名称吗?或者有任何解决方法吗?

解决方法:

广泛的编辑

对于MySQL和sqlite后端,Django 1.6(包括1.11)存在一个解决方案,选项为ForeignKey.db_constraint=False和显式Meta.db_table.如果数据库名称和表名称由”'(对于MySQL)或”'(对于其他数据库)引用,例如db_table =’“db2”.“table2”’).那么它不会被引用更多并且有问题的查询是由Django ORM编译的.更好的类似解决方案是db_table =’db2“.”table2′(不仅允许连接,而且它也更接近跨越数据库约束迁移的一个问题)

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

查询集:

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

Django中的所有db后端都支持该查询解析,但其他必要步骤必须由后端单独讨论.我试图更广泛地回答因为我找到了similar important question.

选项’db_constraint’是迁移所必需的,因为Django无法创建参考完整性约束
ADD外键table1(fk_id)REFERENCES db2.table2(id),
但它是can be created manually for MySQL.

特定后端的问题是,是否可以在运行时将另一个数据库连接到缺省值,并且是否支持跨数据库外键.这些模型也是可写的.间接连接的数据库应该用作managed = False的遗留数据库(因为只有一个表django_migrations用于迁移跟踪仅在直接连接的数据库中创建.此表应该只描述同一数据库中的表.)外键的索引可以但是,如果数据库系统支持此类索引,则可以在托管端自动创建.

Sqlite3:它必须在运行时附加到另一个默认的sqlite3数据库(回答SQLite – How do you join tables from different databases),最好是信号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)

那么它当然不需要数据库路由器和普通的django … ForeignKey可以与db_constraint = False一起使用.一个优点是,如果表名在数据库之间是唯一的,则不需要“db_table”.

在MySQL foreign keys between different databases中很容易.所有命令(如SELECT,INSERT,DELETE)都支持任何数据库名称,而不会先附加它们.

这个问题是关于遗留数据库的.然而,我对迁移也有一些有趣的结果.

标签:python,mysql,django,django-database
来源: https://codeday.me/bug/20191001/1840468.html