SQLAlchemy:在两个MySQL模式中查询表
我需要在同一个MySQL实例中对分布在两个不同模式(schema)下的表进行一些查询。用SQL来做这件事很简单,只需要在表名之前加上模式名(假设模式名是a和b):
SELECT upeople.id AS upeople_id
FROM a.upeople JOIN b.changes ON upeople.id = changes.changed_by
在SQLAlchemy中,似乎可以通过使用两个引擎来实现这个功能。看完文档后,我觉得下面的代码应该可以工作(绑定部分负责处理使用两个不同模式名的复杂情况):
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.query import Query
Base = declarative_base(cls=DeferredReflection)
BaseId = declarative_base(cls=DeferredReflection)
class Changes(Base):
__tablename__ = 'changes'
class UPeople(BaseId):
__tablename__ = 'upeople'
database='mysql://jgb:XXX@localhost/a'
id_database='mysql://jgb:XXX@localhost/b'
engine = create_engine(database)
id_engine = create_engine(id_database)
Base.prepare(engine)
BaseId.prepare(id_engine)
bindings = {Changes: engine,
UPeople: id_engine}
Session = sessionmaker(binds=bindings)
session = Session()
q = session.query(UPeople.id).join(Changes,
UPeople.id == Changes.changed_by)
print q
但是,它并没有成功。当我执行q(比如q.all())时,它失败了。“print q”显示的查询没有模式名:
SELECT upeople.id AS upeople_id
FROM upeople JOIN changes ON upeople.id = changes.changed_by
而不是我期待的结果:
SELECT upeople.id AS upeople_id
FROM a.upeople JOIN b.changes ON upeople.id = changes.changed_by
我漏掉了什么呢?
顺便说一下,我发现通过添加table_args,就像这样:
class Changes(Base):
__tablename__ = 'changes'
__table_args__ = {'schema': 'a'}
(对两个表都这样做),它就能工作了。但我想知道为什么其他的代码不行……而且,我在声明表的类时并不知道模式名,这意味着我根本无法使用table_args这个方法……
所以,总结一下:如何以“正确的方式”查询来自两个模式的表,如果可能的话,我希望在声明表类时不需要包含模式名,而是在定义引擎和会话时再处理?
1 个回答
1
我没找到为什么那段代码不工作的原因,我倾向于认为这是SQLAlchemy里的一个bug。不过,我找到了一种让类似代码正常工作的办法。最终,这个办法依赖于使用__table_args__
来定义数据库的结构。但是因为我在运行时才知道结构的名字,所以我动态地定义了表的类,使用了下面这个函数:
def table_factory (name, tablename, schemaname):
table_class = type(
name,
(Base,),
dict (
__tablename__ = tablename,
__table_args__ = {'schema': schemaname}
)
)
return table_class
这个函数创建了一个名为“name”的类,用于名为“tablename”的表,如果指定了结构名的话,还会包含这个结构名。
使用这个解决方案,下面的代码和问题中的代码非常相似:
Base = declarative_base()
database='mysql://jgb:XXX@localhost/'
Changes = table_factory (name = "Changes",
tablename = "changes",
schemaname = 'a')
UPeople = table_factory (name = "UPeople",
tablename = "upeople",
schemaname = 'b')
engine = create_engine(database)
Base.prepare(engine)
Session = sessionmaker(bind=engine)
session = Session()
q = session.query(UPeople.id).join(Changes,
UPeople.id == Changes.changed_by)
print q
现在,它打印出:
SELECT b.upeople.id AS b_upeople_id
FROM b.upeople JOIN a.changes ON b.upeople.id = a.changes.changed_by
这其中包含了结构名,正如我所期望的那样。