У меня есть приложение, которое отслеживает события с веб-сайтов, и часть пользовательского интерфейса показывает количество событий на веб-сайт за определенный период времени. Вот как выглядят модели:
class Website(models.Model):
name = models.CharField(max_length=64)
url = models.TextField()
class Event(models.Model):
website = models.ForeignKey(Website, related_name="events")
created_at = models.DateTimeField(default=timezone.now)
ip_address = models.CharField(max_length=64)
status = models.CharField(max_length=16)
message = models.CharField(max_length=128)
Эти веб-сайты генерируют тысячи событий в день, поэтому таблица Event
довольно велика по сравнению с другими таблицами. Вот как выглядит запрос, который я пытаюсь сгенерировать:
eargs = {
"website": OuterRef("pk"),
"created_at__gte": some_start_time,
"created_at__lt": some_end_time
}
events = Event.objects.filter(**eargs).values("website")
events_count = events.annotate(c=Count("*").values("c")[:1]
websites = Website.objects.annotate(events=Coalesce(Subquery(events_count,
output_field=IntegerField()), 0)
Как я уже говорил, таблица Events
содержит миллионы строк. Для небольшого количества веб-сайтов этот запрос не займет много времени. Но когда есть 100 или более сайтов, это занимает довольно много времени. Я провел некоторое профилирование, и база данных (внутренне) запрашивает счетчик для каждого веб-сайта. Поэтому, если у меня есть 100 веб-сайтов, база данных делает 100 запросов для генерации счетчиков (есть еще один запрос от Django, но Postgres внутренне делает эти 100 подзапросов).
То, что я хотел бы сделать, это предварительная выборка этих подсчетов, потому что когда я запускаю следующий необработанный SQL, это на самом деле довольно быстро:
SELECT
website_id,
COUNT(*)
FROM
myapp_events
WHERE
created_at >= some_start_time AND created_at < some_end_time
GROUP BY
website_id;
Есть ли какой-нибудь возможный способ предварительно выбрать этот запрос и по-прежнему использовать результаты в моем QuerySet? Или я все об этом ошибся? Похоже, что это было бы обычным делом, и мне трудно обдумать, как это ускорить.