TLTR: Django 在SQL查询中不包含数据库名称,我能以某种方式强制它执行此操作还是有解决方法?
长版:
我有两个传统 MySQL 数据库(注意:我对数据库布局没有影响)我为创建一个只读API < / strong>在Django 1.11和python 3.6上使用DRF
我使用此处建议的SpanningForeignKey字段解决MyISAM数据库的参照完整性限制问题:https://stackoverflow.com/a/32078727/7933618
我试图通过DB1上的表通过ManyToMany将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
这当然给了我一个错误&#34;表&#39; DB2.throughtable&#39;不存在&#34;因为贯穿式是在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 在查询中包含数据库名称吗?或者有什么解决方法吗?
答案 0 :(得分:8)
广泛的编辑
Django 1.6 + (包括1.11)针对 MySQL 和 sqlite 后端的解决方案,通过选项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中的所有数据库后端支持该查询解析,但其他必要步骤必须由后端单独讨论。我试图更广泛地回答,因为我找到了similar important question。
选项'db_constraint'是迁移所必需的,因为Django无法创建参考完整性约束
ADD foreign key table1(fk_id) REFERENCES db2.table2(id)
,
但它为MySQL can be created manually。
特定后端的问题是,是否可以在运行时将另一个数据库连接到缺省值,并且是否支持跨数据库外键。这些模型也是可写的。间接连接的数据库应该用作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等所有命令都支持任何数据库名称,而不会先附加它们。
这个问题是关于遗留数据库的。然而,我对迁移也有一些有趣的结果。
答案 1 :(得分:6)
我有与 PostgreSQL 类似的设置。利用search_path
在Django中实现跨模式引用(postgres中的模式= mysql中的数据库)。不幸的是,似乎MySQL没有这样的机制。
但是,您可以试试运气creating views。在一个引用其他数据库的数据库中创建视图,使用它来选择数据。我认为这是最好的选择,因为无论如何你都希望你的数据是只读的。
然而,这不是一个完美的解决方案,在某些情况下执行raw queries可能会更有用。
UPD:使用PostgreSQL提供有关我的设置的模式详细信息(稍后由bounty请求)。我在MySQL文档中找不到像search_path
这样的东西。
快速介绍
PostgreSQL有Schemas。它们是MySQL数据库的同义词。因此,如果您是MySQL用户,则想象性地将单词“schema”替换为单词“database”。请求可以在模式之间连接表,创建外键等...每个用户(角色)都有一个search_path:
此变量 [search_path] 指定搜索模式的顺序 一个对象(表,数据类型,函数等)由一个简单的引用 名称未指定架构。
特别注意“没有指定架构”,因为这正是Django所做的。
示例:旧版数据库
假设我们得到了coupe遗留模式,并且由于我们不允许修改它们,我们还需要一个新模式来存储NM关系。
old1
是第一个遗留架构,它有old1_table
(为方便起见,它也是型号名称)old2
是第二个旧架构,它有old2_table
django_schema
是一个新的,它将存储所需的NM关系我们需要做的就是:
alter role django_user set search_path = django_schema, old1, old2;
就是这样。是的,那很简单。 Django没有在任何地方指定的模式(“数据库”)的名称。 Django实际上不知道发生了什么,一切都是由幕后的PostgreSQL管理的。由于django_schema
是列表中的第一个,因此将在那里创建新表。所以下面的代码 - &gt;
class Throughtable(models.Model):
a_id = models.ForeignKey('old1_table', ...)
b_id = models.ForeignKey('old2_table', ...)
- &GT;将导致创建引用throughtable
和old1_table
的表old2_table
的迁移。
问题:如果您碰巧有几个具有相同名称的表,您将需要重命名它们或者仍然欺骗Django在表名中使用一个点。
答案 2 :(得分:3)
Django确实能够使用多个数据库。请参阅https://docs.djangoproject.com/en/1.11/topics/db/multi-db/。
您还可以在Django中使用原始SQL查询。见https://docs.djangoproject.com/en/1.11/topics/db/sql/。