基于其他行的Sqlalchemy column_property

时间:2019-06-25 20:09:08

标签: python sqlalchemy

我有模型expenseincome,它们分别具有多对多关系refundsexpenses。我想在expense表上创建一个column_property,该表称为refunded_amount,并根据以下伪代码填充(费用由相同的refund.income_id过滤):

expenses = [exp1, exp2]
payment = income.amount
for exp in expenses:
    amt = exp.amount
    if amt <= payment:
        exp.refunded_amount += amt
        payment -= amt
    else:
        exp.refunded_amount = payment
        payment = 0

如何将其转换为sqlalchemy并将其包括在refunded_amount column_property的select语句中?当前是这些模型,但是像这样refunded_amount的每笔费用都包含与该特定收入相关的所有费用的总和。

refund_table = Table(
    "refund", Base.metadata,
    Column("expense_id", Integer, ForeignKey("expense.id"), primary_key=True),
    Column("income_id", Integer, ForeignKey("income.id"), primary_key=True)
)


class Income(Base):
    __tablename__ = "income"

    id = Column(Integer, primary_key=True)
    date = Column(DateTime, nullable=False)
    amount = Column(Float)
    refund = Column(Boolean, default=False, nullable=False)
    expenses = relationship("Expense", secondary=refund_table,
                            back_populates="refunds")


class Expense(Base):
    __tablename__ = "expense"

    id = Column(Integer, primary_key=True)
    date = Column(DateTime, nullable=False)
    refunds = relationship("Income", secondary=refund_table,
                           back_populates="expenses")

    refunded_amount = column_property(
        select(
            [func.sum(Income.amount)],
            and_(
                refund_table.c.expense_id == id,
                refund_table.c.income_id == Income.id
            ),
            refund_table
        ).label("refunded_amount")
    )

重要的是,如果可能的话,我想使用声明性语句,并避免使用经典的Python @property,因为我可以使用该对象的当前会话,因为如sqlalchemy docs所述:

  

无格式描述符方法作为最后的手段很有用,但在通常情况下比混合属性和列属性方法性能差,因为它需要在每次访问时发出SQL查询。

1 个答案:

答案 0 :(得分:0)

如果仅取Expense.amountsum(Income.amount)中的最小值即可返回所需结果:

refunded_amount = column_property(
    select(
        [func.min(amount, func.sum(Income.amount))],
        and_(
            refund_table.c.expense_id == id,
            refund_table.c.income_id == Income.id
        ),
        refund_table
    ).label("refunded_amount")
)