【问题标题】:SQLAlchemy: How can I create a hybrid property for the disjunction of columns?SQLAlchemy:如何为列的分离创建混合属性?
【发布时间】:2015-06-15 10:42:51
【问题描述】:

我有一个 User 类,它存储一个“first_name”列和一个“alias”列来描述一个人的名字和他们的昵称。我希望能够通过他们的名字或别名来查询这个类来查找用户,所以我这样写了我的类:

from sqlalchemy import Column, Integer, String, create_engine, orm
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property


engine = create_engine('sqlite:///:memory:')
Base = declarative_base()
Session = orm.sessionmaker(engine)


class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    first_name = Column(String(64))
    alias = Column(String(64), nullable=True)

    @hybrid_property
    def name(self):
        return self.alias or self.first_name

    def __repr__(self):
        return 'User("{}" a.k.a. "{}")'.format(self.first_name, self.alias)


if __name__ == "__main__":
    Base.metadata.create_all(engine)

这几乎可行。我可以按名字搜索用户。但我无法通过别名搜索用户。

>>> session = Session()
>>> user = User(first_name="Edward", alias="Ed")
>>> session.add(user)
>>> session.flush()
>>> session.query(User).filter_by(name="Edward").first()
User("Edward" a.k.a. "Ed")
>>> session.query(User).filter_by(name="Ed").first()
>>>

这是有道理的——name 生成的表达式不是我想要的。

>>> print session.query(User).filter_by(name="Ed")
SELECT "user".id AS user_id, "user".first_name AS user_first_name, "user".alias AS user_alias
FROM "user"
WHERE "user".first_name = :first_name_1

SQLAlchemy docs on hybrid properties 似乎暗示解决方案是使用hybrid_property.expression() 更改生成的表达式。但是,我看不出返回什么表达式有意义。

我的第一个想法是返回某种OR

from sqlalchemy.sql import or_

@name.expression
def name(cls):
    return or_(cls.first_name, cls.alias)

但它生成的表达式是可以预见的错误:

SELECT "user".id AS user_id, "user".first_name AS user_first_name, "user".alias AS user_alias
FROM "user"
WHERE ("user".first_name OR "user".alias) = :param_1

我怎样才能做到这一点?

【问题讨论】:

    标签: python properties sqlalchemy


    【解决方案1】:

    经过更多的研究和实验,解决方案在“Building Custom Comparators”部分进行了描述。您可以使用 Comparator 对象来实现自定义比较:

    class Either(Comparator):
        def __init__(self, left, right):
            self.left = left
            self.right = right
    
        def __eq__(self, other):
            return or_(other == self.left, other == self.right)
    
    
    class User(Base):
        # ...
    
        @name.comparator
        def name(self):
            return Either(self.first_name, self.alias)
    

    按预期工作:

    >>> user
    User("Edward" a.k.a. "Ed")
    >>> print session.query(User).filter_by(name="Edward")
    SELECT "user".id AS user_id, "user".first_name AS user_first_name, "user".alias AS user_alias
    FROM "user"
    WHERE "user".first_name = :first_name_1 OR "user".alias = :alias_1
    >>> print session.query(User).filter_by(name="Edward").first()
    User("Edward" a.k.a. "Ed")
    >>> print session.query(User).filter_by(name="Ed").first()
    User("Edward" a.k.a. "Ed")
    

    我认为通过研究“Hybrid Value Objects”,这里还有更多的改进空间,但是,它仍然在逃避我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-04
      • 2012-12-14
      • 1970-01-01
      • 2020-07-02
      • 1970-01-01
      • 2013-01-08
      • 2022-01-22
      相关资源
      最近更新 更多