如何在 django rest 框架中的嵌套序列化器相关对象上使用 prefetch_related?
Posted
技术标签:
【中文标题】如何在 django rest 框架中的嵌套序列化器相关对象上使用 prefetch_related?【英文标题】:How to use prefetch_related on nested serializer related objects in django rest framework? 【发布时间】: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')
调试工具栏中仍有重复查询。
【问题讨论】:
【参考方案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')
)
【讨论】:
以上是关于如何在 django rest 框架中的嵌套序列化器相关对象上使用 prefetch_related?的主要内容,如果未能解决你的问题,请参考以下文章
如何获取主键相关字段嵌套序列化器django rest框架的所有值
django rest 框架 POST 请求因嵌套序列化而失败