Учитывая следующие модели, я хотел бы добавить сортируемый столбец к 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
может быть отображается на несколько Organisation
с, следовательно, отношение многих ко многим.
Я добавил upcoming_payments
к column_sortable_list
в Flask -Admin ModelView
, но при сортировке по этому столбцу результаты ошибочны c: 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
, являются правильными, как и результат, если Я запускаю это в оболочке:
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
или подзапроса, но я в замешательстве.
ОБНОВЛЕНИЕ: Все, спасибо Илья Эвериле за то, что он направил меня к ответу:
@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')