Как оптимизировать сериализаторы django -rest-framework с несколькими наборами запросов в SerializerMethodField () - PullRequest
0 голосов
/ 07 августа 2020

Я новичок в django -rest-framework, и у меня есть проект, в котором мне нужно оптимизировать время отклика существующей конечной точки API. Используя django -debug-toolbar, нашел несколько способов использования prefetch_related () для уменьшения количества запросов SQL. Проблема в том, что я обнаружил, что дизайн сериализатора должен иметь несколько SerializerMethodField для получения значений статистики, а использование prefetch_related работает только для all (), но во втором поле метода сериализатора с filter () все еще запрашивается для каждого класса l oop. Вы можете увидеть образец кода ниже (это не код спецификаций c, а что-то вроде этого).

serializer.py:

class ClassStatisticsSerializer(ModelBaseSerializer):
    total_sessions = serializers.SerializerMethodField()
    activity_count = serializers.SerializerMethodField()

    def get_total_sessions(self, instance):
        sessions = instance.classactivity_set.all()
        date = self.context.get('date')
        if date:
            sessions = sessions.filter(date=date)

        if len(sessions) == 0:
            return None

        return sessions.count()

    def get_activity_count(self, instance):
        activities = instance.classactivity_set.filter(is_present=False)
        date = self.context.get('date')
        if date:
            activities = activities.filter(date=date)

        if len(activities) == 0:
            return None

        return activities.count()

    class Meta:
        model = Class
        fields = (
           'id',
           'batch',
           'type,
           'total_sessions'
           'activity_count'
        )

views.py:

class ClassStatisticsList(APIView):
     def get(self, request, *args, **kwargs):
          queryset = Class.objects.all().prefetch_related('classactivity_set')
          serializer = ClassStatisticsSerializer()
          return Response(serializer, status=status.HTTP_200_OK)

Здесь, когда я проверяю запросы SQL на панели инструментов отладки django, get_total_sessions был запрошен один раз на SQL, но get_activity_count не был, поскольку это еще один запрос запроса на SQL и запрашивается для каждого класса l oop. На данный момент о создании новой модели не может быть и речи, поэтому я застрял на том, как правильно выполнить предварительную выборку второго запроса. Надеюсь, вы сможете предложить возможный способ решения этой проблемы. Заранее спасибо, ребята.

1 Ответ

0 голосов
/ 07 августа 2020

Вы можете аннотировать количество объектов с помощью набора запросов, а затем передать его через инициализацию класса сериализатора. Примерно так:

from django.db.models import Count, Q

condition = {}
if date:
    condition = {'date':date}

queryset = Class.objects.all().annotate(
    total_sessions=Count(
        'classactivity',
        filter=Q(**condition),
        distinct=True
    ),
    activity_count=Count(
        'classactivity',
        filter=Q(**condition)&Q(classactivity__is_present=False),
        distinct=True
    )
)

Затем вам нужно удалить SerializerMethodField s из сериализатора, простого IntegerField должно быть достаточно для получения данных из набора запросов без дополнительных обращений к базе данных:

class ClassStatisticsSerializer(ModelBaseSerializer):
    total_sessions = serializers.IntegerField()
    activity_count = serializers.IntegerField()
...