【问题标题】:How to use prefetch_related on nested serializer related objects in django rest framework?如何在 django rest 框架中的嵌套序列化器相关对象上使用 prefetch_related?
【发布时间】:2021-06-06 15:15:48
【问题描述】:

从下面的模型中,我尝试在课程序列化程序上返回特定大学不同课程类型的计数,并尽可能减少查询次数。

型号

class University(models.Model):
    name = models.CharField(max_length=255)

    class Meta:
        ordering = ['name']
        verbose_name_plural = 'Universities'

    def __str__(self):
        return "%s" % self.name



class CourseType(models.Model):
    name = models.CharField("Course Name", max_length=255)

    def __str__(self):
        return self.name


class Course(models.Model):
    name = models.CharField("Course Name", max_length=255)
    course_type = models.ForeignKey(
        CourseType,
        null=True,
        blank=True,
        related_name='course_type',
        on_delete=models.SET_NULL)
    university = models.ForeignKey(
        University,
        null=True,
        blank=True,
        related_name='courses',
        on_delete=models.SET_NULL
    )

    def __str__(self):
        return self.name

序列化器

class UniversitySerializer(serializers.ModelSerializer):
    course_type_count = serializers.SerializerMethodField(
        'get_course_type_count')

    class Meta:
        model = University
        fields = "__all__"

    def get_course_type_count(self, obj):
        course_count = {}

        # This one is causing extra query
        courses_type = obj.courses.all().values('course_type__name').annotate(
            total=Count('id')).order_by('course_type')
        for course_type in courses_type:
            course_count[f"{course_type['course_type__name']}"] = course_type['total']
        return course_count


class CourseSerializer(serializers.ModelSerializer):
    course_type = serializers.CharField(source='course_type.name')
    university = UniversitySerializer(read_only=True)

    @staticmethod
    def setup_eager_execution(qs):
        qs = qs.select_related('university')
        qs = qs.select_related('course_type')

        # This is what i've tried
        qs = qs.prefetch_related(
            Prefetch('university__courses__course_type',
                     queryset=qs.prefetch_related('course_type'))
        )


        return qs

    class Meta:
        model = Course
        fields = "__all__"

我试过了。

在课程序列化程序中,我将预取 CourseType 为:

qs = qs.prefetch_related(
        Prefetch('university__courses__course_type',
                   queryset=qs.prefetch_related('course_type'))
    )

在上面的 ORM 调用中,我预取课程类型并遍历对象。如下UniversitySerializer

courses_type = obj.courses.all().values('course_type__name').annotate(
   total=Count('id')).order_by('course_type')

调试工具栏中仍有重复查询。

【问题讨论】:

    标签: django-rest-framework query-optimization django-serializer


    【解决方案1】:

    您应该注意的一件事是,只要您访问.all() 或与Prefetch 对象查询集匹配的查询集,prefetch_related 就可以工作。在您的情况下,get_course_type_count 进行注释以计算计数,因此无法使用预取对象。我的建议是在 python 中进行计数计算。

    from collections import defaultdict
    
    
    class UniversitySerializer(serializers.ModelSerializer):
        ...
    
        def get_course_type_count(self, obj):
            course_count = defaultdict(int)
    
            for course in obj.courses.all():
                course_count[course.course_type.name] += 1
    
            return course_count
    

    只使用这个预取就足够了

    qs = qs.prefetch_related(
        Prefetch('university__courses__course_type')
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-05
      • 2015-02-25
      • 2018-12-13
      • 1970-01-01
      • 1970-01-01
      • 2020-04-11
      • 2022-06-25
      • 2021-06-23
      相关资源
      最近更新 更多