Django - фильтровать набор запросов, пока не будет достигнута сумма

Представим себе модель под названием Roll. Она хранит результат броска шестигранного кубика (D6):

class Roll(models.Model):
    outcome = models.PositiveSmallIntegerField('Roll', null=False, blank=False, default=1)

Есть много рулонов, например:

print(list(Roll.objects.all().values_list('outcome', flat=True)))
>>> [1, 5, 6, 3, 5, 4, 4, 3, 2]

Теперь, как мне получить последние N строк, у которых Sum('outcome') достигает произвольной суммы, не зацикливаясь тем или иным способом, пока эта сумма не будет достигнута?

Если мы представим рулоны:

pk    | outcome | (accumulated sum)
1     | 3       | 3
2     | 2       | 5
3     | 6       | 11
4     | 1       | 12
5     | 5       | 17
6     | 4       | 21
7     | 3       | 24
8     | 4       | 29
9     | 5       | 34
10    | 1       | 35

и произвольной суммы 20, то запрос должен выбрать пк 6, так как накопленная сумма теперь достигла необходимой суммы.

Может ли работать что-то похожее на нижеприведенное?

amount = 100
Roll.objects.annotate(
    accumulated_sum=Subquery(
        Roll.objects.filter(
            pk__lte=OuterRef('pk')
        ).values('pk').order_by('pk').annotate(
            sum=Sum('outcome', distinct=True)
        ).values(
            'sum'
        )[:1]
    )
).filter(
    accumulated_sum__gte=amount
)

Это может помочь аннотировать accumuated_sum:

from django.db.models import Subquery, IntegerField

class SQSum(Subquery):
    output_field = IntegerField()
    template = "(SELECT sum(outcome) from (%(subquery)s) _sum)"

accumulated_subquery = SQSum(
    Roll.objects.filter(
        pk__lte=OuterRef('pk')
    ).values("outcome")
)

Roll.objects.annotate(accumulated_sum=accumulated_subquery).filter(accumulated_sum__gte=amount)
Вернуться на верх