SQLAlchemy 自定义查询列

发布于 2024-08-25 02:55:57 字数 696 浏览 8 评论 0原文

我有一个如下定义的声明性表:

class Transaction(Base):
    __tablename__ = "transactions"
    id = Column(Integer, primary_key=True)
    account_id = Column(Integer)
    transfer_account_id = Column(Integer)
    amount = Column(Numeric(12, 2))
    ...

查询应该是:

SELECT id, (CASE WHEN transfer_account_id=1 THEN -amount ELSE amount) AS amount
FROM transactions
WHERE account_id = 1 OR transfer_account_id = 1

我的代码是:

query = Transaction.query.filter_by(account_id=1, transfer_account_id=1)
query = query.add_column(case(...).label("amount"))

但它不会替换 amount 列。

我已经尝试这样做几个小时了,但我不想使用原始 SQL。

I have a declarative table defined like this:

class Transaction(Base):
    __tablename__ = "transactions"
    id = Column(Integer, primary_key=True)
    account_id = Column(Integer)
    transfer_account_id = Column(Integer)
    amount = Column(Numeric(12, 2))
    ...

The query should be:

SELECT id, (CASE WHEN transfer_account_id=1 THEN -amount ELSE amount) AS amount
FROM transactions
WHERE account_id = 1 OR transfer_account_id = 1

My code is:

query = Transaction.query.filter_by(account_id=1, transfer_account_id=1)
query = query.add_column(case(...).label("amount"))

But it doesn't replace the amount column.

Been trying to do this with for hours and I don't want to use raw SQL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沧桑㈠ 2024-09-01 02:55:59

您所做的任何查询都不会替换原始的 amount 列。但是您可以使用以下查询加载另一列:

q = session.query(Transaction,
                  case([(Transaction.transfer_account_id==1, -1*Transaction.amount)], else_=Transaction.amount).label('special_amount')
                  )
q = q.filter(or_(Transaction.account_id==1, Transaction.transfer_account_id==1))

这不会仅返回 Transaction 对象,而是返回 tuple(Transaction, Decimal)


但如果您希望此属性成为您的对象,然后:
由于您的 case when ... 函数完全独立于 WHERE 中的条件,我建议您按以下方式更改代码:

1)为您添加一个属性对象,它执行 case when ... 检查,如下所示:

@property
def special_amount(self):
    return -self.amount if self.transfer_account_id == 1 else self.amount

您还可以完全包装对金额的特殊处理,并提供 setter 属性:

@special_amount.setter
def special_amount(self, value):
    if self.transfer_account_id is None:
        raise Exception('Cannot decide on special handling, because transfer_account_id is not set')
    self.amount = -value if self.transfer_account_id == 1 else value

2) 修复您的查询,使其仅包含一个过滤子句or_ 子句(看起来您的查询根本不起作用):

q = session.query(Transaction).filter(
    or_(Transaction.account_id==1, 
        Transaction.transfer_account_id==1)
)

# then get your results with the proper amount sign:
for t in q.all():
    print q.id, q.special_amount

Any query you do will not replace original amount column. But you can load another column using following query:

q = session.query(Transaction,
                  case([(Transaction.transfer_account_id==1, -1*Transaction.amount)], else_=Transaction.amount).label('special_amount')
                  )
q = q.filter(or_(Transaction.account_id==1, Transaction.transfer_account_id==1))

This will not return only Transaction objects, but rather tuple(Transaction, Decimal)


But if you want this property be part of your object, then:
Since your case when ... function is completely independent from the condition in WHERE, I would suggest that you change your code in following way:

1) add a property to you object, which does the case when ... check as following:

@property
def special_amount(self):
    return -self.amount if self.transfer_account_id == 1 else self.amount

You can completely wrap this special handling of the amount providing a setter property as well:

@special_amount.setter
def special_amount(self, value):
    if self.transfer_account_id is None:
        raise Exception('Cannot decide on special handling, because transfer_account_id is not set')
    self.amount = -value if self.transfer_account_id == 1 else value

2) fix your query to only have a filter clause with or_ clause (it looks like your query does not work at all):

q = session.query(Transaction).filter(
    or_(Transaction.account_id==1, 
        Transaction.transfer_account_id==1)
)

# then get your results with the proper amount sign:
for t in q.all():
    print q.id, q.special_amount
何止钟意 2024-09-01 02:55:59

您正在寻找的构造称为column_property。您可以使用辅助映射器来实际替换金额列。您确定不直接将负值存储在数据库中或为“更正”列指定不同的名称,不会让事情变得太困难吗?

from sqlalchemy.orm import mapper, column_property
wrongmapper = sqlalchemy.orm.mapper(Transaction, Transaction.__table,
    non_primary = True,
    properties = {'amount':
        column_property(case([(Transaction.transfer_account_id==1, -1*Transaction.amount)], 
        else_=Transaction.amount)})

Session.query(wrongmapper).filter(...)

The construct you are looking for is called column_property. You could use a secondary mapper to actually replace the amount column. Are you sure you are not making things too difficult for yourself by not just storing the negative values in the database directly or giving the "corrected" column a different name?

from sqlalchemy.orm import mapper, column_property
wrongmapper = sqlalchemy.orm.mapper(Transaction, Transaction.__table,
    non_primary = True,
    properties = {'amount':
        column_property(case([(Transaction.transfer_account_id==1, -1*Transaction.amount)], 
        else_=Transaction.amount)})

Session.query(wrongmapper).filter(...)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文