SQLAlchemy custom query column

早过忘川 提交于 2019-12-10 10:26:08

问题


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.


回答1:


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



回答2:


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(...)


来源:https://stackoverflow.com/questions/2444679/sqlalchemy-custom-query-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!