【问题标题】:Erratic sort order for hybrid_property sum expression in Flask-Admin using SQLAlchemy使用 SQLAlchemy 的 Flask-Admin 中的 hybrid_property sum 表达式的排序顺序不稳定
【发布时间】:2020-02-27 21:19:14
【问题描述】:

鉴于以下模型,我想向 Flask Admin 添加一个可排序的列,显示 Organisation 的所有即将付款:

organisation_payments = db.Table(
    'organisation_payments',
    db.Column('payment_id', MyUUID(), db.ForeignKey('payment.id')),
    db.Column('organisation_id', MyUUID(), db.ForeignKey('organisation.id'))
)

class Organisation(db.Model):
    id = Column(ModelUUID(), primary_key=True)

    @hybrid_property
    def upcoming_payments(self):
        payments = Payment.query.filter(
            Payment.organisations.any(id=self.id),
            Payment.status == 'active'
        ).all()

        return sum([payment.amount for payment in payments])

    @upcoming_payments.expression
    def upcoming_payments(cls):
        return select([
            func.sum(Payment.amount)
        ]).where(and_(
            organisation_payments.c.organisation_id == cls.id,
            Payment.status == 'active'
        )).label('upcoming_payments')

class Payment(db.Model):
    id = Column(ModelUUID(), primary_key=True)
    amount = db.Column(db.Integer())
    status = db.Column(db.Unicode(255), default='active')
    organisations = db.relationship(
        'Organisation',
        secondary=organisation_payments,
        backref=db.backref('payments', lazy='dynamic')
    )

请注意,理论上一个Payment 可以映射到多个Organisations,因此是多对多关系。

我已在 Flask-Admin ModelView 中将 upcoming_payments 添加到 column_sortable_list,但是在对该列进行排序时,结果不稳定:https://i.stack.imgur.com/VxrAE.png

这是代码的最小版本,但我也尝试过:

  1. 使用coalesce 强制0 用于没有即将付款的行
  2. 使用as_scalar() 代替.label()
  3. Payment.organisations.any(id=cls.id) 代替 organisation_payments.c.organisation_id == cls.id(这会产生更加混乱和不一致的结果,而对 asc/desc 进行排序没有任何区别)

从常规 hybrid_property 返回的值 正确,如果我在 shell 中运行它,结果也是如此:

stmt = select([
        func.sum(Payment.amount)
    ]).where(and_(
        Payment.organisations.any(id=org.id),
        Payment.status == 'active',
    ))

res = db.engine.execute(stmt)
res.fetchone() 
(4036200L,)

但是,如果我运行这个结果是非常不准确的:

stmt = select([
        func.sum(Payment.amount)
    ]).where(and_(
        organisation_payments.c.organisation_id == org.id,
        Payment.status == 'active',
    ))

res = db.engine.execute(stmt)
res.fetchone() 
(1204440000L,)

但是any() 或关联表方法都不会在 Flask-Admin 中返回正确的排序顺序。我在这里做错了什么?我觉得我一定错过了 distinct 或子查询,但我很难过。

更新:感谢 Ilja Everilä 引导我找到答案:

    @upcoming_payments.expression
    def upcoming_payments(cls):
        q = select([coalesce(func.sum(Payment.amount), 0)])
        q = q.where(and_(
            organisation_payments.c.charity_id == cls.id,
            Payment.status == 'active',
            Payment.deleted.isnot(True)
        ))

        j = Payment.__table__.join(organisation_payments)
        q = q.select_from(j)

        return q.label('upcoming_donations')

【问题讨论】:

  • 在混合表达式的查询中,Payment 和 organization_payments 之间似乎缺少连接,导致它们之间存在交叉连接。
  • @IljaEverilä 就是这样,谢谢!我会用正确的答案编辑帖子。非常感谢!
  • 您也可以(应该)发布实际解决方案作为答案,而不仅仅是编辑问题。

标签: python sqlalchemy


【解决方案1】:

更新:感谢 Ilja Everilä 引导我找到答案:

    @upcoming_payments.expression
    def upcoming_payments(cls):
        q = select([coalesce(func.sum(Payment.amount), 0)])
        q = q.where(and_(
            organisation_payments.c.charity_id == cls.id,
            Payment.status == 'active',
            Payment.deleted.isnot(True)
        ))

        j = Payment.__table__.join(organisation_payments)
        q = q.select_from(j)

        return q.label('upcoming_donations')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 2014-07-31
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 2011-05-20
    相关资源
    最近更新 更多