Django аннотирует количество сложных подзапросов
У меня такая модель:
class Visit(models.Model):
...
device_id = models.CharField(...)
created_at = models.DateTimeField(auto_now_add=True)
...
Эта модель в основном обозначает посещение места, device_id однозначно идентифицирует человека, и один и тот же человек может посещать место несколько раз, поэтому в этой таблице создается несколько объектов.
Я строю аналитические методы на основе этой модели. Одна вещь, которую я хочу сделать, - это показать количество людей, которые посетили за последние 30 дней только один раз. При базовом подходе я могу выдавать запрос к базе данных на каждый день следующим образом:
for date in dates:
start_time = date
end_time = date + datetime.timedelta(days=1)
Visitor.objects.filter(
site__in=node_descendants,
created_at__gte=start_date,
created_at__lt=end_date)
).values('device_id').annotate(visit_count=Count('device_id')).filter(visit_count=1).count()
Теперь я хочу рассчитать данные для каждого дня в 30-дневном диапазоне с помощью одного запроса, используя такой подзапрос:
single_visitor_query = Visitor.objects.filter(
created_at__date=OuterRef('truncated_date')
).order_by().values('device_id')
.annotate(visit_count=Count('device_id')).filter(visit_count=1)
.annotate(count=Count('*')).values('count')
chart_data_query = Visitor.objects.filter(
created_at__gte=start_date, # Start of month
created_at__lt=end_date, # End of month
).annotate(
truncated_date=TruncDay('created_at')
).values('truncated_date').distinct().annotate(
single_visitor_count=Subquery(single_visitor_query[:1], output_field=PositiveIntegerField()
)
У меня есть пара проблем с этим подходом:
- count, полученный в результате подзапроса, - это не количество всех элементов, имеющих visit_count=1, но все они равны 1. Я знаю, что подзапрос несколько не работает, но я не вижу, как я могу исправить это, чтобы вернуть счетчик для всех элементов, передающих фильтр.
- Я хотел бы, чтобы подзапрос запускался для каждого truncated_date (в данном случае 30 раз), но похоже, что он выполняется для каждого экземпляра, который проходит начальный фильтр. Я проверил полученный sql-запрос, и он не группируется по "truncated_date". Есть идеи, почему это может быть так?