Python SQLAlchemy映射联接表';列到一个对象
我有三个表:UserTypeMapper、User和SystemAdmin。在我的Python SQLAlchemy映射联接表';列到一个对象,python,orm,sqlalchemy,pyramid,Python,Orm,Sqlalchemy,Pyramid,我有三个表:UserTypeMapper、User和SystemAdmin。在我的get_user方法中,根据UserTypeMapper.is_admin行,我然后查询user或SystemAdmin表。user\u id行与user和SystemAdmin表中的主键id相关 class UserTypeMapper(Base): __tablename__ = 'user_type_mapper' id = Column(BigInteger, primary_key=Tr
get_user
方法中,根据UserTypeMapper.is_admin行,我然后查询user或SystemAdmin表。user\u id
行与user和SystemAdmin表中的主键id
相关
class UserTypeMapper(Base):
__tablename__ = 'user_type_mapper'
id = Column(BigInteger, primary_key=True)
is_admin = Column(Boolean, default=False)
user_id = Column(BigInteger, nullable=False)
class SystemAdmin(Base):
__tablename__ = 'system_admin'
id = Column(BigInteger, primary_key=True)
name = Column(Unicode)
email = Column(Unicode)
class User(Base):
__tablename__ = 'user'
id = Column(BigInteger, primary_key=True)
name = Column(Unicode)
email = Column(Unicode)
我希望能够从一个查询中获得任何用户–系统管理员或普通用户,因此我在user
或SystemAdmin
上进行连接,具体取决于is\u admin
行。例如:
DBSession.query(UserTypeMapper, SystemAdmin).join(SystemAdmin, UserTypeMapper.user_id==SystemAdmin.id).first()
及
这很好用;但是,我希望能够访问这些,就像这样:
>>> my_admin_obj.is_admin
True
>>> my_admin_obj.name
Bob Smith
对
>>> my_user_obj.is_admin
False
>>> my_user_obj.name
Bob Stevens
目前,我必须指定:my\u user\u obj.UserTypeMapper.is\u admin
和my\u user\u obj.user.name
。根据我所阅读的内容,我需要映射表,这样就不需要指定属性属于哪个表。我的问题是,我不明白如何指定它,因为我有两个潜在的表,例如name
属性可能来自这两个表
这就是我所指的例子:
我怎样才能做到这一点?谢谢。您已经了解了为什么“双用途外键”是一个
有一个与此相关的问题,你还没有完全指出;无法使用外键约束强制数据处于有效状态。您希望确保UserTypeMapper中的每一行都有一个something,但“something”不是任何一个表。从形式上讲,您希望功能依赖于
用户类型映射器
→(系统管理
×1)∪ (用户
×0)
但是大多数sql数据库不允许您编写一个外键约束来表达这一点
它看起来很复杂,因为它很复杂
让我们想想我们真正想说的话;“每个系统管理员
都应该是用户
;或者
system\u admin
→user
在sql中,可以这样写:
CREATE TABLE user (
id INTEGER PRIMARY KEY,
name VARCHAR,
email VARCHAR
);
CREATE TABLE system_admin (
user_id INTEGER PRIMARY KEY REFERENCES user(id)
);
或者,用sqlalchemy声明式
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
class SystemAdmin(Base):
__tablename__ = 'system_admin'
user_id = Column(ForeignKey(User.id), primary_key=True)
这个模式允许我们问什么样的问题
- “是否有名为‘john doe’的系统管理员?”
- “有多少用户?有多少系统管理员?”
我希望你能明白为什么上面的设计比你在问题中描述的更可取;但很可能你没有选择(只有在这种情况下,如果你仍然觉得你拥有的更好,请改进你的问题),您仍然可以将该数据塞进一个python对象中,这将非常难以使用,方法是提供一个到表的替代映射;特别是遵循第一个等式中的粗略结构的映射 我们需要两次提到UserTypeMapper,一次用于联盟的每一方,为此,我们需要给出别名
>>> from sqlalchemy.orm import aliased
>>> utm1 = aliased(UserTypeMapper)
>>> utm2 = aliased(UserTypeMapper)
对于联合体,将每个别名连接到相应的表中:由于SystemAdmin
和User
具有相同的列,因此我们不需要详细描述它们,但如果它们完全不同,我们需要通过明确提及每一列使它们“联合兼容”;这只是一个练习
>>> utm_sa = Query([utm1, SystemAdmin]).join(SystemAdmin, (utm1.user_id == SystemAdmin.id) & (utm1.is_admin == True))
>>> utm_u = Query([utm2, User]).join(User, (utm2.user_id == User.id) & (utm2.is_admin == False))
然后我们把他们连在一起
>>> print utm_sa.union(utm_u)
SELECT anon_1.user_type_mapper_1_id AS anon_1_user_type_mapper_1_id, anon_1.user_type_mapper_1_is_admin AS anon_1_user_type_mapper_1_is_admin, anon_1.user_type_mapper_1_user_id AS anon_1_user_type_mapper_1_user_id, anon_1.system_admin_id AS anon_1_system_admin_id, anon_1.system_admin_name AS anon_1_system_admin_name, anon_1.system_admin_email AS anon_1_system_admin_email
FROM (SELECT user_type_mapper_1.id AS user_type_mapper_1_id, user_type_mapper_1.is_admin AS user_type_mapper_1_is_admin, user_type_mapper_1.user_id AS user_type_mapper_1_user_id, system_admin.id AS system_admin_id, system_admin.name AS system_admin_name, system_admin.email AS system_admin_email
FROM user_type_mapper AS user_type_mapper_1 JOIN system_admin ON user_type_mapper_1.user_id = system_admin.id AND user_type_mapper_1.is_admin = 1 UNION SELECT user_type_mapper_2.id AS user_type_mapper_2_id, user_type_mapper_2.is_admin AS user_type_mapper_2_is_admin, user_type_mapper_2.user_id AS user_type_mapper_2_user_id, "user".id AS user_id, "user".name AS user_name, "user".email AS user_email
FROM user_type_mapper AS user_type_mapper_2 JOIN "user" ON user_type_mapper_2.user_id = "user".id AND user_type_mapper_2.is_admin = 0) AS anon_1
虽然理论上可以将这一切封装到一个看起来有点像标准sqlalchemy orm的python类中,但我肯定不会这样做,需要做大量的工作才能实现零回报。两点:1.我刚开始与星展银行合作,我正在购买您链接的那本书。我对它的外观非常满意。2.这是一个非常好的答案,我将接受您最初的建议。它更有意义,而且更易于使用。非常感谢!(注意:我会在19小时后奖励奖金。)@JohnZ:即使是有一些数据库经验的人也会提出这种设计;一些流行的ORM鼓励这种设计并生成这种精确的模式(如ActiveRecord)也无济于事。
>>> from sqlalchemy.orm import aliased
>>> utm1 = aliased(UserTypeMapper)
>>> utm2 = aliased(UserTypeMapper)
>>> utm_sa = Query([utm1, SystemAdmin]).join(SystemAdmin, (utm1.user_id == SystemAdmin.id) & (utm1.is_admin == True))
>>> utm_u = Query([utm2, User]).join(User, (utm2.user_id == User.id) & (utm2.is_admin == False))
>>> print utm_sa.union(utm_u)
SELECT anon_1.user_type_mapper_1_id AS anon_1_user_type_mapper_1_id, anon_1.user_type_mapper_1_is_admin AS anon_1_user_type_mapper_1_is_admin, anon_1.user_type_mapper_1_user_id AS anon_1_user_type_mapper_1_user_id, anon_1.system_admin_id AS anon_1_system_admin_id, anon_1.system_admin_name AS anon_1_system_admin_name, anon_1.system_admin_email AS anon_1_system_admin_email
FROM (SELECT user_type_mapper_1.id AS user_type_mapper_1_id, user_type_mapper_1.is_admin AS user_type_mapper_1_is_admin, user_type_mapper_1.user_id AS user_type_mapper_1_user_id, system_admin.id AS system_admin_id, system_admin.name AS system_admin_name, system_admin.email AS system_admin_email
FROM user_type_mapper AS user_type_mapper_1 JOIN system_admin ON user_type_mapper_1.user_id = system_admin.id AND user_type_mapper_1.is_admin = 1 UNION SELECT user_type_mapper_2.id AS user_type_mapper_2_id, user_type_mapper_2.is_admin AS user_type_mapper_2_is_admin, user_type_mapper_2.user_id AS user_type_mapper_2_user_id, "user".id AS user_id, "user".name AS user_name, "user".email AS user_email
FROM user_type_mapper AS user_type_mapper_2 JOIN "user" ON user_type_mapper_2.user_id = "user".id AND user_type_mapper_2.is_admin = 0) AS anon_1