Медленный ответ от 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

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