Django Агрегированные минимальные и максимальные динамические диапазоны
У меня следующая модель:
class Claim:
amount = models.PositiveIntegerField()
Я пытаюсь создать API, который динамически отправляет ответ таким образом, чтобы диапазоны сумм были динамическими.
Например, моя минимальная сумма претензии составляет 100, а максимальная - 1000. Я хотел показать JSON таким образом:
{
"100-150":2,
"150-250:3,
"250-400:1,
"400-500":5,
"above_500":12
}
Я пытался сделать это, предполагая, что диапазон моих данных находится между 1 и 2000, но это становится бесполезным, если моя минимальная сумма находится между 10000 и 100000.
d = Claim.objects.aggregate(upto_500=Count('pk', filter=Q(amount__lte=500)),
above_500__below_1000=Count('pk', filter=Q(amount__range=[501, 999])),
above_1000__below_2000=Count('pk', filter=Q(amount__range=[1000, 2000])),
above_2000=Count('pk', filter=Q(amount__gte=2000))
)
Есть идеи, как мы можем сделать динамический способ получения диапазонов сумм и передачи их во фронтенд?
from django.db.models import Q
upto_500=Count('pk', filter=Q(amount__lte=500))
above_500__below_1000=Count('pk', filter=Q(amount__range=(501, 999)))
above_1000__below_2000=Count('pk', filter=Q(amount__range=(1000, 2000)))
above_2000=Count('pk', filter=Q(amount__gte=2000))
claim = Claim.objects.annotate(upto_500=upto_500).annotate(above_500__below_1000=above_500__below_1000).annotate(above_1000__below_2000=above_1000__below_2000).annotate(above_2000=above_2000)
print(claim[0].upto_500)
это количество меньше 500 и так далее.
теперь вы можете легко создать JSON из него.
Я думаю, это то, что вы ищете:
from django.db.models import Max, Min, Count, Q
claim_min_max = Claim.objects.aggregate(Min("amount"), Max("amount"))
amount_min = claim_min_max["amount__min"]
amount_max = claim_min_max["amount__max"]
step = 100
elements = range(amount_min, amount_max, step)
pairs = []
for i in range(len(elements)):
try:
pairs.append(elements[i], elements[i + 1])
except IndexError:
break
aggregate_pairs = {
f"from_{_from}_to_{_to}": Count("pk", filter=Q(amount__range=[_from, _to]))
for _from, _to in pairs
}
queryset = Claim.objects.aggregate(**aggregate_pairs)
Динамический способ подсчета элементов в партиях
Я решил этот вопрос таким образом
claims = Claim.objects.all()
claims = self.filter_queryset(claims)
claim_min_max = claims.aggregate(Min("amount"), Max("amount"))
if (claim_min_max["amount__min"]) and claim_min_max["amount__max"] is not None:
amount_min = int(claim_min_max["amount__min"])
amount_max = int(claim_min_max["amount__max"])
if (amount_max - amount_min) != 0:
width_size = int((amount_max - amount_min) / 5)
data = []
for datas in range(amount_min, amount_max, width_size):
response = {'from': datas, 'to': datas + width_size, 'count': claims.filter(
amount__range=[datas, datas + width_size]).count()}
data.append(response)
return Response({"data": data})
else:
return Response({})
else:
return Response({
})
Позвольте одному значению выступать в качестве идентификатора для диапазона, а затем группируйте по этому значению. Таким идентификатором может быть коэффициент, когда он делится на размер шага.
Например, если у вас есть значения: [121, 131, 170, 215, 390]
, преобразуйте его в [100, 100, 150, 200, 350]
(при условии, что шаг=50) и подсчитайте значения.
from django.db.models import Count, F, IntegerField
from django.db.models.functions import Cast, Floor
step = 50
# Ignore the min/max logic altogether if you don't care about getting these explicitly
min_value = 100
max_value = 500
claims = Claim.objects.all()
claims = claims.filter(amount__gte=min_value, amount__lt=max_value)
counts = (
claims.annotate(
value=Cast(
Floor(F('amount') / (step * 1.0)) * step,
output_field=IntegerField(),
)
)
.values('value')
.annotate(count=Count('*'))
.values_list('value', 'count')
)
ranges = {}
for value, count in counts:
range_name = f"{value}-{value + step}"
ranges[range_name] = count
ranges[f"<{min_value}"] = Claim.objects.filter(amount__lt=min_value).count()
ranges[f">={max_value}"] = Claim.objects.filter(amount__gte=max_value).count()