用于在sqlAlchemy

时间:2018-01-25 14:30:09

标签: python sqlalchemy single-table-inheritance

在flask-sqlAlchemy中,我有一个类User,其中包含status列,如下所示:

class User(Model):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
    status = Column(String(255))
    age = Column(Integer)
    yet_another_property = Column(String(255))

我现在想要一个模型ActiveUser,它代表user表中status'active'的所有条目。在MySQL中使用,这将是

SELECT * FROM user 
    WHERE
       user.status = 'active';

我认为,这应该以某种方式使用ActiveUser作为User的子类并通过单表继承(如http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#single-table-inheritance所述),如下所示:

class User(Model):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
    status = Column(String(255))
    age = Column(Integer)
    yet_another_property = Column(String(255))

    __mapper_args__ = {
        'polymorphic_on':status,
        'polymorphic_identity':'user'
    }

class ActiveUser(User):
    __mapper_args__ = {
        'polymorphic_on':status,
        'polymorphic_identity':'active'
    }

我的第一个问题是:这会有效吗?

它变得有点复杂,因为实际上我想过滤多个属性。所以,实际上,我希望有一个类ActiveAdultUser,它代表所有处于active状态且年龄大于或等于18的用户。再次,在MySQL中说:

SELECT * FROM user 
    WHERE
       user.status = 'active'
    AND
       user.age >= 18;

我的第二个问题是:那么,我该怎样做

当然,我知道,我可以通过将Users应用于.filter(and_(User.status == 'active', User.age >=18))上的查询,对有效成年人User进行查询。但是为了清洁代码,我希望在模型级别上使用此过滤器。

我还想过覆盖ActiveUser模型上的查询功能,但这看起来相当hacky并且我不知道在任何情况下我是否可以依赖它。

2 个答案:

答案 0 :(得分:1)

我不认为你可以用多态来做到这一点,因为你不能把"大于"比较那里。您只能拥有固定的polymorphic_identity。你也不能在两个不同的领域上只变形,只有一个。

对我来说,这是一个你计划简化"一些已经相对简单的东西。具有两个过滤条件的SQL查询几乎不是一个复杂甚至是混乱的语句。

如果您坚持要做到这一点,那么正确的方法是继承Query并在那里写条件。 https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery给出了如何执行此操作的示例。

就个人而言,我只想在我的查询中添加两个过滤器。它远不如在未来版本中可能会改变的子类化更加混乱和脆弱。查询语法可能更持久。

删除冗余代码的另一种方法是预编写查询:

q = (User.age >= 18, User.status == "active")
foo = Session.query(User).filter(*q).all()

甚至

q = Session.query(User).filter(User.age >= 18, User.status == "active")
foo = q.filter(add your additional filters here).all()
# or if no additional filters needed
foo = q.all()

如果您在代码的合适位置定义q,则可以在整个程序中使用它。这将消除在每个查询中重复这两个过滤条件的需要。

答案 1 :(得分:1)

您可以生成multiple mappers for one class - 位于文档的non-traditional mappings部分:

  

但是,有一类称为非主要映射器的映射器,允许其他映射器与类关联,但使用范围有限。此范围通常适用于能够从备用表或可选单元加载行,但仍然生成使用主映射最终持久化的类。

因此,您需要生成非主要映射器ActiveUser而不是子类:

In [9]: ActiveUser = mapper(
   ...:     User,
   ...:     User.__table__.select().
   ...:         where(User.status == "active").
   ...:         alias(),
   ...:     non_primary=True)
   ...:     

In [10]: print(session.query(ActiveUser))
SELECT anon_1.id AS anon_1_id, anon_1.status AS anon_1_status, anon_1.age AS anon_1_age, anon_1.yet_another_property AS anon_1_yet_another_property 
FROM (SELECT user.id AS id, user.status AS status, user.age AS age, user.yet_another_property AS yet_another_property 
FROM user 
WHERE user.status = ?) AS anon_1

另一方面,所有这些可能都是不必要的,您可以在需要时查询活跃用户。