Пользовательский столбец SQLAlchemy - PullRequest
3 голосов
/ 15 марта 2010

У меня есть декларативная таблица, определенная так:

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.

Ответы [ 2 ]

1 голос
/ 15 марта 2010

Любой запрос, который вы делаете, не заменяет исходный столбец 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

Вы можете полностью обернуть эту специальную обработку суммы, также предоставляя свойство сеттера:

@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
1 голос
/ 15 марта 2010

Конструкция, которую вы ищете, называется 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(...)
...