如何在django-rest框架中对嵌套的序列化程序相关对象使用prefetch_related



根据下面的模型,我试图用尽可能少的查询数量在课程序列化程序上返回特定大学的不同课程类型的计数。

型号

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')

调试工具栏中仍然有一个重复的查询。

需要注意的一点是,只要访问.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')
)

最新更新