根据下面的模型,我试图用尽可能少的查询数量在课程序列化程序上返回特定大学的不同课程类型的计数。
型号
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')
)