Медленный ответ от Django Rest Framework с аннотированным набором запросов
Я использую Django Rest Framework и просто пытаюсь отфильтровать объекты, которые имеют более 0 связанных элементов, но как только я добавляю фильтр в аннотацию, включающий время даты, время ответа увеличивается с миллисекунд до 90 секунд или около того.
Вот код, который занимает целую вечность:-
class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
queryset = EventVenue.objects.annotate(
num_slots=models.Count(
"eventblock__eventslot",
filter=Q(eventblock__status='active') & Q(
eventblock__eventslot__start_time__gt=timezone.now()
+ timedelta(days=2)
)
)
).filter(num_slots__gt=0)
Если я удалю фильтр, включающий поле DateTimeField (start_time)...
class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
queryset = EventVenue.objects.annotate(
num_slots=models.Count(
"eventblock__eventslot",
filter=Q(eventblock__status='active')
)
).filter(num_slots__gt=0)
это очень быстро.
Но присутствие этого фильтра времени даты замедляет его до такой степени, что он становится бесполезным.
Это никак не связано с поиском в базе данных - он выполняет 8 таких поисков, так что все в порядке.
SQL-запрос выглядит нормально, и результаты правильные - просто это занимает так много времени!
Есть идеи?
Вам следует использовать надлежащие инструменты отладки (например, silk), если вы гонитесь за увеличением производительности.
Из вашего набора вопросов я понял, что у вас есть четыре модели: EventSlot, EventBlock, EventVenue и Event. EventSlot имеет FK к Event и EventBlock, а EventBlock имеет FK к EventVenue.
Если это так, то вы можете увидеть прирост производительности от следующих действий.
class EventVenueViewSet(viewsets.ReadOnlyModelViewSet):
qs = EventVenue.objects.all()
qs = qs.annotate(
num_slots=models.Count(
"eventblock__eventslot",
filter=Q(eventblock__eventslot__event=None)
)
)
qs = qs.filter(num_slots__gt=0)
slot_qs = EventSlot.objects.select_related("event")
block_qs = EventBlock.objects.prefetch_related(
Prefetch("eventslot_set", queryest=slot_qs)
)
qs = qs.prefetch_related(Prefetch("eventblock_set", queryset=block_qs))
Это позволит удалить один запрос и одно отображение предварительной выборки.
Если вы не сериализуете num_slots
и используете его только для фильтрации, вам следует рассмотреть возможность использования .alias()
вместо .annotate()
, поскольку это удалит его из select