在SQLAlchemy中映射'伪'对象
我不太确定这个东西叫什么,因为对我来说是新的,但我想做的是:
我在数据库里有两个表:TableA 和 TableB。TableA 有一个主键 a_id 和另一个字段叫 a_code。TableB 有一个主键 b_id 和另一个字段叫 b_code。
我在我的 sqlalchemy 代码里已经把这两个表关联起来了,运行得很好。我想创建一个第三个对象叫 TableC,这个对象在我的数据库里并不存在,但它包含了 a_code 和 b_code 的组合,类似这样:
class TableC:
a_code = String
b_code = String
然后我想查询 TableC,像这样:
TableC.query.filter(and_(
TableC.a_code == x,
TableC.b_code == y)).all()
问题 1)这种东西有什么名字吗? 2)我该如何进行映射(如果能用声明式的方式就更好了)?
3 个回答
其实没有“虚拟表”这个概念,但你可以发送一个查询,把多个表的数据“连接”在一起。这可能是你想要的最接近的方式。
比如,在sqlalchemy/elixir中,你可以这样做(这和你展示的差不多,只是我们不是在查询一个“虚拟”表):
result = session.query(TableA, TableB).filter(TableA.a_code==x).filter(TableB.b_code==y).all()
这和SQL中的内连接类似,只是过滤条件稍有不同。这不会给你一个sqlalchemy的表对象,但会返回每个真实表中的对象列表。
看起来SQLAlchemy让你可以把任意的查询结果映射到一个类上。比如说,来自SQLAlchemy:一个类 - 两个表的内容:
usersaddresses = sql.join(t_users, t_addresses,
t_users.c.id == t_addresses.c.user_id)
class UserAddress(object):
def __repr__(self):
return "<FullUser(%s,%s,%s)" % (self.id, self.name, self.address)
mapper(UserAddress, usersaddresses, properties={
'id': [t_users.c.id, t_addresses.c.user_id],
})
f = session.query(UserAddress).filter_by(name='Hagar').one()
我对你想表达的查询并没有完全理解,不知道是要合并(union)还是连接(join)或者其他什么,但不管怎样,确实可以把任何可以传给数据库并返回行的东西映射出来。
我假设你想要的是表A和表B的某种合并,也就是把A中的所有行和B中的所有行都拿出来。如果你能提供更多关于你想表达的数据结构的信息,我可以更容易地调整这个概念。
我们先来设置真实的表和映射它们的类,采用声明式的方式。
from sqlalchemy import *
import sqlalchemy.ext.declarative
Base = sqlalchemy.ext.declarative.declarative_base()
class TableA(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
a_code = Column(String)
class TableB(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
b_code = Column(String)
因为我们用了声明式,所以实际上没有可以直接操作的表实例,这在接下来的步骤中是必要的。有很多方法可以访问这些表,但我更喜欢用sqlalchemy的映射反射方法,因为这样无论类是怎么映射的都能工作。
from sqlalchemy.orm.attributes import manager_of_class
a_table = manager_of_class(TableA).mapper.mapped_table
b_table = manager_of_class(TableB).mapper.mapped_table
接下来,我们需要一个实际的SQL表达式来表示我们感兴趣的数据。这是一个合并,结果会得到和第一个类中定义的列一样的列,像id
和a_code
。我们可以给它重新命名,但这在这个例子中并不是很重要。
ab_view_sel = sqlalchemy.alias(a_table.select().union(b_table.select()))
最后,我们把一个类映射到这个表达式上。虽然也可以用声明式来做,但其实这样写的代码比经典的映射方式还要多,并不是更少。注意这个类是从object
继承的,而不是base
。
class ViewAB(object):
pass
sqlalchemy.orm.mapper(ViewAB, ab_view_sel)
就这样了。当然,这里有一些限制;最明显的是没有简单的方法可以把ViewAB
的实例保存回数据库。