mas*_*oda 12 python mysql django django-database
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
Run Code Online (Sandbox Code Playgroud)
这当然给了我一个错误"表'DB2.throughtable'不存在",因为viatable在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
Run Code Online (Sandbox Code Playgroud)
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')
Run Code Online (Sandbox Code Playgroud)
app2的 模型db2_app/models.py:(DB2)
class TableB(models.Model):
id = models.AutoField(primary_key=True)
# some other fields
Run Code Online (Sandbox Code Playgroud)
数据库路由:
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
Run Code Online (Sandbox Code Playgroud)
我可以强制Django 在查询中包含数据库名称吗?或者有任何解决方法吗?
广泛的编辑
对于MySQL和sqlite后端,Django 1.6+(包括1.11)存在一个解决方案,可选择ForeignKey.db_constraint = False和explicit .如果数据库名和表名被引用的"'"(用于MySQL的)或者"""(对于其他数据库),例如),然后它不引用更多的点是出报价.有效的查询被编译Django ORM.一个更好的类似解决方案是(不仅允许连接,而且它也是一个更接近跨数据库约束迁移的问题)Meta.db_tabledb_table = '"db2"."table2"'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
Run Code Online (Sandbox Code Playgroud)
查询集:
>>> 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'
Run Code Online (Sandbox Code Playgroud)
Django中的所有db后端都支持该查询解析,但其他必要步骤必须由后端单独讨论.我试图更广泛地回答,因为我发现了一个类似的重要问题.
选项'db_constraint'是迁移所必需的,因为Django无法创建引用完整性约束
ADD foreign key table1(fk_id) REFERENCES db2.table2(id),
但可以为MySQL 手动创建它.
特定后端的问题是,是否可以在运行时将另一个数据库连接到缺省值,并且是否支持跨数据库外键.这些模型也是可写的.间接连接的数据库应该用作遗留数据库managed=False(因为只有一个django_migrations用于迁移跟踪的表只在直接连接的数据库中创建.此表应该只描述同一数据库中的表.)但是,外键的索引可以自动创建如果数据库系统支持此类索引,则在托管端.
Sqlite3:它必须在运行时附加到另一个默认的sqlite3数据库(回答SQLite - 如何连接来自不同数据库的表),最好是信号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)
Run Code Online (Sandbox Code Playgroud)
然后它当然不需要数据库路由器,并且正常django...ForeignKey可以与db_constraint = False一起使用.一个优点是,如果表名在数据库之间是唯一的,则不需要"db_table".
在MySQL中 ,不同数据库之间的外键很容易.所有命令(如SELECT,INSERT,DELETE)都支持任何数据库名称,而不会先附加它们.
这个问题是关于遗留数据库的.然而,我对迁移也有一些有趣的结果.
我有一个与PostgreSQL类似的设置.利用search_pathDjango中的跨模式引用(postgres中的模式= mysql中的数据库).不幸的是,似乎MySQL没有这样的机制.
但是,您可以尝试为它创建视图.在一个引用其他数据库的数据库中创建视图,使用它来选择数据.我认为这是最好的选择,因为无论如何你都希望你的数据是只读的.
然而,它不是一个完美的解决方案,在某些情况下执行原始查询可能更有用.
UPD:提供有关我使用PostgreSQL进行设置的模式详细信息(稍后由bounty提出请求).我search_path在MySQL文档中找不到任何东西.
快速介绍
PostgreSQL有Schema.它们是MySQL数据库的同义词.因此,如果您是MySQL用户,则想象性地将单词"schema"替换为单词"database".请求可以在模式之间连接表,创建外键等...每个用户(角色)都有一个search_path:
此变量[search_path]指定在未指定架构的简单名称引用对象(表,数据类型,函数等)时搜索架构的顺序.
特别注意"没有指定架构",因为这正是Django所做的.
示例:旧数据库
假设我们得到了coupe遗留模式,并且由于我们不允许修改它们,我们还需要一个新的模式来存储NM关系.
old1它是第一个传统模式,它有old1_table(为了方便起见,它也是模型名称)old2 它是第二个传统架构 old2_tabledjango_schema 是一个新的,它将存储所需的NM关系我们需要做的就是:
alter role django_user set search_path = django_schema, old1, old2;
Run Code Online (Sandbox Code Playgroud)
就是这个.是的,那很简单.Django没有在任何地方指定的模式("数据库")的名称.Django实际上不知道发生了什么,一切都是由幕后的PostgreSQL管理的.由于django_schema是列表中的第一个,因此将在那里创建新表.所以下面的代码 - >
class Throughtable(models.Model):
a_id = models.ForeignKey('old1_table', ...)
b_id = models.ForeignKey('old2_table', ...)
Run Code Online (Sandbox Code Playgroud)
- >将导致创建throughtable引用old1_table和的表的迁移old2_table.
问题:如果您碰巧有几个具有相同名称的表,您将需要重命名它们或仍然欺骗Django使用表名内的点.
| 归档时间: |
|
| 查看次数: |
1775 次 |
| 最近记录: |