Я использую SQLAlchemy v2 и имею две модели: Пользователь и Транзакция. Здесь Transaction относится к денежным транзакциям между пользователями, а не к транзакциям базы данных. Я определил гибридное свойство в модели User, которое вычисляет баланс для каждого пользователя на основе его входящих и исходящих денежных транзакций. Однако я сталкиваюсь с проблемами при попытке использовать это гибридное свойство, где или фильтр для запроса пользователей на основе их балансов.
Вот как я определил свой модели и гибридное свойство:
from __future__ import annotations
from decimal import Decimal
from typing import List
from sqlalchemy import ForeignKey, SQLColumnExpression, create_engine, func, select
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import (
DeclarativeBase,
Mapped,
mapped_column,
relationship,
sessionmaker,
)
class Base(DeclarativeBase):
pass
class Transaction(Base):
__tablename__ = "transactions"
id: Mapped[int] = mapped_column(primary_key=True)
amount: Mapped[float]
sender_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
recipient_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
sender: Mapped[User] = relationship(foreign_keys=[sender_id])
recipient: Mapped[User] = relationship(foreign_keys=[recipient_id])
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
sent_transactions: Mapped[List[Transaction]] = relationship(
foreign_keys=[Transaction.sender_id], overlaps="sender"
)
received_transactions: Mapped[List[Transaction]] = relationship(
foreign_keys=[Transaction.recipient_id], overlaps="recipient"
)
@hybrid_property
def balance(self) -> Decimal:
incoming = sum(txn.amount for txn in self.received_transactions)
outgoing = sum(txn.amount for txn in self.sent_transactions)
balance = incoming - outgoing
return balance
@balance.inplace.expression
@classmethod
def _balance_expression(cls) -> SQLColumnExpression[Decimal]:
return select(
(
func.coalesce(
select(func.sum(Transaction.amount))
.where(Transaction.recipient_id == 1)
.scalar_subquery(),
0,
)
- func.coalesce(
select(func.sum(Transaction.amount))
.where(Transaction.sender_id == 1)
.scalar_subquery(),
0,
).label("balance")
)
)
А вот код, который я запустил для запроса пользователей с балансом больше 1 (работает не так, как ожидалось):
engine = create_engine("sqlite:///db.db")
Session = sessionmaker(engine)
with Session() as session:
stmt = select(User).where(User.balance > 1)
session.execute(stmt)
После запуска вышеуказанного компакт-диска возникли следующие ошибки:
Traceback (most recent call last):
File "c:\Users\Nima\Desktop\userbalance\main.py", line 111, in
stmt = select(User).where(User.balance > 1)
^^^^^^^^^^^^^^^^
File "C:\Users\Nima\AppData\Local\Programs\Python\Python311\Lib\site-packages\sqlalchemy\sql\operators.py", line 629, in __gt__
return self.operate(gt, other)
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Nima\AppData\Local\Programs\Python\Python311\Lib\site-packages\sqlalchemy\orm\attributes.py", line 453, in operate
return op(self.comparator, *other, **kwargs) # type: ignore[no-any-return] # noqa: E501
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Nima\AppData\Local\Programs\Python\Python311\Lib\site-packages\sqlalchemy\sql\operators.py", line 629, in __gt__
return self.operate(gt, other)
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Nima\AppData\Local\Programs\Python\Python311\Lib\site-packages\sqlalchemy\ext\hybrid.py", line 1509, in operate
return op(self.expression, *other, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: '>' not supported between instances of 'Select' and 'int'
Подробнее здесь: https://stackoverflow.com/questions/784 ... ter-method