如何在 SQLAlchemy 中为这个混合 属性 实现 SQL 级表达式?
How to implement SQL level expression for this hybrid property in SQLAlchemy?
我有一个分类帐 table 和一个相应的 python class。
我使用SQLAlchemy定义模型,如下,
class Ledger(Base):
__tablename__ = 'ledger'
currency_exchange_rate_lookup = {('CNY', 'CAD'): 0.2}
amount = Column(Numeric(10, 2), nullable=False)
currency = Column(String, nullable=False)
payment_method = Column(String)
notes = Column(UnicodeText)
@hybrid_property
def amountInCAD(self):
if self.currency == 'CAD':
return self.amount
exchange_rate = self.currency_exchange_rate_lookup[(self.currency, 'CAD')]
CAD_value = self.amount * Decimal(exchange_rate)
CAD_value = round(CAD_value, 2)
return CAD_value
@amountInCAD.expression
def amountInCAD(cls):
amount = cls.__table__.c.amount
currency_name = cls.__table__.c.currency
exchange_rate = cls.currency_exchange_rate_lookup[(currency_name, 'CAD')]
return case([
(cls.currency == 'CAD', amount),
], else_ = round((amount * Decimal(exchange_rate)),2))
现在如您所见,我想创建一个名为 "amountInCAD" 的混合 属性。 Python 级别 getter 似乎工作正常。但是 SQL 表达式不起作用。
现在,如果我 运行 这样的查询:
>>>db_session.query(Ledger).filter(Ledger.amountInCAD > 1000)
SQL炼金术给我这个错误:
File "ledger_db.py", line 43, in amountInCAD
exchange_rate = cls.currency_exchange_rate_lookup[(currency_name, 'CAD')]
KeyError: (Column('currency', String(), table=<ledger>, nullable=False), 'CAD')
我研究了 SQLAlchemy 关于混合 属性 的在线文档。 http://docs.sqlalchemy.org/en/latest/orm/mapped_sql_expr.html#using-a-hybrid
将我的代码与示例代码进行比较,我不明白为什么我的代码不起作用。如果在官方例子中,cls.firstname
可以引用一列值,为什么我的代码中cls.__table__.c.currency
只有returns一个Column
不是它的值?
cls.firstname
不是"refer to value",而是Column
。 example 中的 cls.firstname + " " + cls.lastname
生成字符串连接 SQL 表达式,如下所示:
firstname || ' ' || lastname
这是混合属性的一部分魔力:它们使编写可在两个域中工作的简单表达式变得相对容易,但在处理 python 实例和在构建 SQL 表达式时。
您可以稍微重新考虑一下您自己的混合体,并在您的 case
表达式中实际将转换选项传递给数据库:
from sqlalchemy import func
...
@amountInCAD.expression
def amountInCAD(cls):
# This builds a list of (predicate, expression) tuples for case. The
# predicates compare each row's `currency` column against the bound
# `from_` currencies in SQL.
exchange_rates = [(cls.currency == from_,
# Note that this does not call python's round, but
# creates an SQL function expression. It also does not
# perform a multiplication, but produces an SQL expression
# `amount * :rate`. Not quite sure
# why you had the Decimal conversion, so kept it.
func.round(cls.amount * Decimal(rate), 2))
for (from_, to_), rate in
cls.currency_exchange_rate_lookup.items()
# Include only conversions to 'CAD'
if to_ == 'CAD']
return case(exchange_rates + [
# The default for 'CAD'
(cls.currency == 'CAD', cls.amount),
])
通过这种方式,您可以将汇率查询作为 CASE
表达式有效地传递给 SQL。
我有一个分类帐 table 和一个相应的 python class。 我使用SQLAlchemy定义模型,如下,
class Ledger(Base):
__tablename__ = 'ledger'
currency_exchange_rate_lookup = {('CNY', 'CAD'): 0.2}
amount = Column(Numeric(10, 2), nullable=False)
currency = Column(String, nullable=False)
payment_method = Column(String)
notes = Column(UnicodeText)
@hybrid_property
def amountInCAD(self):
if self.currency == 'CAD':
return self.amount
exchange_rate = self.currency_exchange_rate_lookup[(self.currency, 'CAD')]
CAD_value = self.amount * Decimal(exchange_rate)
CAD_value = round(CAD_value, 2)
return CAD_value
@amountInCAD.expression
def amountInCAD(cls):
amount = cls.__table__.c.amount
currency_name = cls.__table__.c.currency
exchange_rate = cls.currency_exchange_rate_lookup[(currency_name, 'CAD')]
return case([
(cls.currency == 'CAD', amount),
], else_ = round((amount * Decimal(exchange_rate)),2))
现在如您所见,我想创建一个名为 "amountInCAD" 的混合 属性。 Python 级别 getter 似乎工作正常。但是 SQL 表达式不起作用。
现在,如果我 运行 这样的查询:
>>>db_session.query(Ledger).filter(Ledger.amountInCAD > 1000)
SQL炼金术给我这个错误:
File "ledger_db.py", line 43, in amountInCAD
exchange_rate = cls.currency_exchange_rate_lookup[(currency_name, 'CAD')]
KeyError: (Column('currency', String(), table=<ledger>, nullable=False), 'CAD')
我研究了 SQLAlchemy 关于混合 属性 的在线文档。 http://docs.sqlalchemy.org/en/latest/orm/mapped_sql_expr.html#using-a-hybrid
将我的代码与示例代码进行比较,我不明白为什么我的代码不起作用。如果在官方例子中,cls.firstname
可以引用一列值,为什么我的代码中cls.__table__.c.currency
只有returns一个Column
不是它的值?
cls.firstname
不是"refer to value",而是Column
。 example 中的 cls.firstname + " " + cls.lastname
生成字符串连接 SQL 表达式,如下所示:
firstname || ' ' || lastname
这是混合属性的一部分魔力:它们使编写可在两个域中工作的简单表达式变得相对容易,但在处理 python 实例和在构建 SQL 表达式时。
您可以稍微重新考虑一下您自己的混合体,并在您的 case
表达式中实际将转换选项传递给数据库:
from sqlalchemy import func
...
@amountInCAD.expression
def amountInCAD(cls):
# This builds a list of (predicate, expression) tuples for case. The
# predicates compare each row's `currency` column against the bound
# `from_` currencies in SQL.
exchange_rates = [(cls.currency == from_,
# Note that this does not call python's round, but
# creates an SQL function expression. It also does not
# perform a multiplication, but produces an SQL expression
# `amount * :rate`. Not quite sure
# why you had the Decimal conversion, so kept it.
func.round(cls.amount * Decimal(rate), 2))
for (from_, to_), rate in
cls.currency_exchange_rate_lookup.items()
# Include only conversions to 'CAD'
if to_ == 'CAD']
return case(exchange_rates + [
# The default for 'CAD'
(cls.currency == 'CAD', cls.amount),
])
通过这种方式,您可以将汇率查询作为 CASE
表达式有效地传递给 SQL。