【发布时间】: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
这是代码的最小版本,但我也尝试过:
- 使用
coalesce强制0用于没有即将付款的行 - 使用
as_scalar()代替.label() -
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