Как я могу объединить строки с помощью конкатената в Django annotate?

У меня есть следующая модель в Django:

class Click(models.Model):
    url = models.ForeignKey(Url, on_delete=models.CASCADE)
    browser = models.CharField(max_length=255)
    platform = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

Я хочу построить запрос, чтобы получить общее количество кликов в день в текущем месяце, а также браузеры и платформы в конкатенированном виде. Например:

[
  {
    'created_at__day': 28,
    'browsers': 'Chrome, Firefox',
    'platforms': 'Windows, Linux',
    'clicks': 4
  }
]

Что я делал до этого момента, так это запрос:

queryset = Click.objects.filter(
    url=url_instance,
    created_at__month=datetime.now().month
).values('created_at__day', 'browser').annotate(
    clicks=Count('created_at'),
)

Как я могу объединить все браузеры и платформы, группируя только по created_at__day?

Неясно, какие именно данные у вас есть, чтобы получить желаемый результат, но если вы используете Postgresql в качестве базы данных, StringAgg или ArrayAgg могут быть тем, что вы ищете.

Если вы хотите исключить дубликаты из ваших агрегированных строк, это становится немного сложнее.

Я думаю, что вы близки, как упомянул @Shabble, StringAgg если вы используете Postresql:

queryset = Click.objects.filter(
    url=url_instance,
    created_at__month=datetime.now().month
).values('created_at__day', 'browser').annotate(
    clicks=Count('created_at'),
    platforms=StringAgg('platform', delimiter=', ', distinct=True),
)

Если нет, вам придется использовать Subquery, и написать соответствующую строку Aggregation для базы данных. Получилось бы что-то вроде:

# Inner query
click_platforms = Click.objects.filter(
    url=url_instance,
    created_at__month=datetime.now().month,
    created_at__day=OuterRef("created_at__day")
).distinct()
# Outer query
queryset = Click.objects.filter(
    url=url_instance,
    created_at__month=datetime.now().month
).values('created_at__day', 'browser').annotate(
    clicks=Count('created_at'),
    platforms=YourAggregation(click_platforms),
)

Я не могу написать YourAggregation, это зависит от того, что доступно в вашей базе данных.

Вернуться на верх