python - Django ManyToMany 通过多个数据库

标签 python mysql django django-database

TLTR: Django 没有在 SQL 查询中包含数据库名称,我可以强制它这样做还是有解决方法?

长版:

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

我正在使用此处建议的 SpanningForeignKey 字段解决 MyISAM 数据库的引用完整性限制:https://stackoverflow.com/a/32078727/7933618

我正在尝试通过 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' 不存在”,因为 throughtable 在 DB1 上,我不知道如何强制 Django 在表前加上 DB 名称。查询应该是:

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 在查询中包含数据库名称吗?或者有什么解决方法吗?

最佳答案

Django 1.6+(包括 1.11)的解决方案适用于 MySQLsqlite 后端,通过选项 ForeignKey.db_constraint=False和明确的 Meta.db_table。如果数据库名和表名被 ' ' (对于 MySQL)或 ' "'(对于其他 db)引用,例如 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 中的所有数据库后端都支持查询解析,但是其他必要的步骤必须由后端单独讨论。我试图更笼统地回答,因为我找到了 similar important question .

迁移需要选项“db_constraint”,因为 Django 无法创建引用完整性约束
添加外键table1(fk_id) REFERENCES db2.table2(id),
但它can be created manually用于 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 - Django ManyToMany 通过多个数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45018277/

相关文章:

python - 在 python gdb 脚本中索引 c++ vector

php - PDO 插入数据库

Python/Ubuntu - 将目录复制到新目录(目录路径不正确?)

Python 数组赋值

python - matplotlib 忽略缺失数据

mysql - 计算时间之间的小时数

mysql - Kubernetes POD 创建问题 mysql PV、PVC 问题、centos 7.3.1611

python - 在序列化器中创建访问另一个模型属性的新字段

python - 在 DjangoRestFramework(DRF) 中,未定义名称 'request'

python - webpack 和 django 找不到静态文件