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". Есть идеи, почему это может быть так?

0 ответов

Другие вопросы по тегам