SQLAlchemy:如何为列的分离创建混合 属性?
SQLAlchemy: How can I create a hybrid property for the disjunction of columns?
我有一个用户 class,它存储一个 "first_name" 列和一个 "alias" 列来描述一个人的名字和他们的昵称。我希望能够查询此 class 以通过他们的 first_name 或他们的别名查找用户,所以我这样写我的 class:
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 seems to suggest the solution is use hybrid_property.expression()改变生成的表达式。但是,我看不出哪种表达方式对 return.
有意义
我的第一个想法是 return 某种 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
我怎样才能完成这项工作?
经过更多的研究和实验,结果在“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”这里有更多的空间改进,但是,它仍然在逃避我。
我有一个用户 class,它存储一个 "first_name" 列和一个 "alias" 列来描述一个人的名字和他们的昵称。我希望能够查询此 class 以通过他们的 first_name 或他们的别名查找用户,所以我这样写我的 class:
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 seems to suggest the solution is use hybrid_property.expression()改变生成的表达式。但是,我看不出哪种表达方式对 return.
有意义我的第一个想法是 return 某种 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
我怎样才能完成这项工作?
经过更多的研究和实验,结果在“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”这里有更多的空间改进,但是,它仍然在逃避我。