Я очень часто сталкиваюсь с подобными задачами, но до сих пор не понимаю как их правильно решать, пример:
Дана вот такая схема моделей:
Задача - вывести 5 клиентов потративших больше всего денег. Каждый клиент описывается следующими полями:
username - логин клиента;
spent_money - сумма потраченных средств за весь период;
gems - список из названий камней, которые купили как минимум двое из списка "5 клиентов, потративших
наибольшую сумму за весь период", и данный клиент является одним из этих покупателей.
Модели:class Deal(models.Model):
customer = models.ForeignKey('Customer', on_delete=models.CASCADE, related_name='deals')
item = models.ForeignKey('Item', on_delete=models.CASCADE, related_name='deals')
total = models.DecimalField(decimal_places=3, max_digits=10, validators=[MinValueValidator(Decimal('0.0'))])
quantity = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
class Customer(models.Model):
name = models.TextField()
class Item(models.Model):
name = models.TextField()
Запрос:customers_data = list(
Deal.objects.values(
'customer__pk',
'customer__name'
).annotate(sum_total=Sum('total')).order_by('-sum_total')[:5]
)
customers_pk = tuple(element['customer__pk'] for element in customers_data)
deals_qs = Deal.objects.filter(customer__pk__in=customers_pk)
items_pk = deals_qs.values(
'item__pk',
).annotate(
item_count=Count('customer__pk', distinct=True),
).filter(
item_count__gte=REPEATED_ITEMS_COUNT,
).values_list('item__pk', flat=True)
repeated_items = deals_qs.filter(
item__pk__in=items_pk,
).values(
'customer__pk',
'item__name',
).order_by(
'customer__pk',
).distinct()
repeated_items_by_customers = defaultdict(list)
for row in repeated_items:
customer_pk, item_name = row['customer__pk'], row['item__name']
repeated_items_by_customers[customer_pk].append(item_name)
result = []
for customer_data in customers_data:
customer_pk = customer_data['customer__pk']
result.append(
[
customer_data['customer__name'],
customer_data['sum_total'],
repeated_items_by_customers[customer_pk],
]
)
Каждый раз я решаю их примерно так. Вопрос - как это сделать правильно?