Commit d9c3d591 by Clinton Blackburn Committed by GitHub

Optimized performance of the Catalog CSV endpoint (#321)

ECOM-5534
parent e48ec419
...@@ -559,7 +559,7 @@ class FlattenedCourseRunWithCourseSerializer(CourseRunSerializer): ...@@ -559,7 +559,7 @@ class FlattenedCourseRunWithCourseSerializer(CourseRunSerializer):
) )
def get_level_type(self, obj): def get_level_type(self, obj):
return obj.course.level_type return obj.level_type.name
def get_course_key(self, obj): def get_course_key(self, obj):
return obj.course.key return obj.course.key
......
...@@ -141,7 +141,7 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi ...@@ -141,7 +141,7 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi
CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2') CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2')
CourseRunFactory(enrollment_end=enrollment_end, course=self.course) CourseRunFactory(enrollment_end=enrollment_end, course=self.course)
with self.assertNumQueries(40): with self.assertNumQueries(41):
response = self.client.get(url) response = self.client.get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertListEqual(response.data['results'], self.serialize_catalog_course(courses, many=True)) self.assertListEqual(response.data['results'], self.serialize_catalog_course(courses, many=True))
...@@ -167,7 +167,7 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi ...@@ -167,7 +167,7 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi
url = reverse('api:v1:catalog-csv', kwargs={'id': self.catalog.id}) url = reverse('api:v1:catalog-csv', kwargs={'id': self.catalog.id})
with self.assertNumQueries(24): with self.assertNumQueries(21):
response = self.client.get(url) response = self.client.get(url)
course_run = self.serialize_catalog_flat_course_run(self.course_run) course_run = self.serialize_catalog_flat_course_run(self.course_run)
......
...@@ -37,16 +37,21 @@ logger = logging.getLogger(__name__) ...@@ -37,16 +37,21 @@ logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
PREFETCH_FIELDS = { PREFETCH_FIELDS = {
'course_run': ( 'course_run': [
'course__partner', 'course__level_type', 'course__programs', 'course__programs__type', 'course__partner', 'course__level_type', 'course__programs', 'course__programs__type',
'course__programs__partner', 'seats', 'transcript_languages', 'seats__currency', 'staff', 'course__programs__partner', 'seats', 'transcript_languages', 'seats__currency', 'staff',
'staff__position', 'staff__position__organization', 'language', 'staff__position', 'staff__position__organization', 'language',
), ],
'course': ( 'course': [
'level_type', 'video', 'programs', 'course_runs', 'subjects', 'prerequisites', 'expected_learning_items', 'level_type', 'video', 'programs', 'course_runs', 'subjects', 'prerequisites', 'expected_learning_items',
'authoring_organizations', 'authoring_organizations__tags', 'authoring_organizations__partner', 'authoring_organizations', 'authoring_organizations__tags', 'authoring_organizations__partner',
'sponsoring_organizations', 'sponsoring_organizations__tags', 'sponsoring_organizations__partner', 'sponsoring_organizations', 'sponsoring_organizations__tags', 'sponsoring_organizations__partner',
), ],
}
SELECT_RELATED_FIELDS = {
'course': ['level_type', 'video', ],
'course_run': ['course', 'language', 'video', ],
} }
...@@ -67,11 +72,11 @@ def prefetch_related_objects_for_courses(queryset): ...@@ -67,11 +72,11 @@ def prefetch_related_objects_for_courses(queryset):
QuerySet QuerySet
""" """
# Prefetch the data for the related course runs # Prefetch the data for the related course runs
course_run_prefetch_fields = PREFETCH_FIELDS['course_run'] course_run_prefetch_fields = PREFETCH_FIELDS['course_run'] + SELECT_RELATED_FIELDS['course_run']
course_run_prefetch_fields = ['course_runs__' + field for field in course_run_prefetch_fields] course_run_prefetch_fields = ['course_runs__' + field for field in course_run_prefetch_fields]
queryset = queryset.prefetch_related(*course_run_prefetch_fields) queryset = queryset.prefetch_related(*course_run_prefetch_fields)
queryset = queryset.select_related('level_type', 'video') queryset = queryset.select_related(*SELECT_RELATED_FIELDS['course'])
queryset = queryset.prefetch_related(*PREFETCH_FIELDS['course']) queryset = queryset.prefetch_related(*PREFETCH_FIELDS['course'])
return queryset return queryset
...@@ -192,11 +197,14 @@ class CatalogViewSet(viewsets.ModelViewSet): ...@@ -192,11 +197,14 @@ class CatalogViewSet(viewsets.ModelViewSet):
serializer: serializers.FlattenedCourseRunWithCourseSerializer serializer: serializers.FlattenedCourseRunWithCourseSerializer
""" """
catalog = self.get_object() catalog = self.get_object()
courses = catalog.courses().active() courses = catalog.courses()
course_runs = [] course_runs = CourseRun.objects.filter(course__in=courses).active().marketable()
for course in courses: # We use select_related and prefetch_related to decrease our database query count
course_runs += list(course.course_runs.active().marketable()) course_runs = course_runs.select_related(*SELECT_RELATED_FIELDS['course_run'])
prefetch_fields = ['course__' + field for field in PREFETCH_FIELDS['course']]
prefetch_fields += PREFETCH_FIELDS['course_run']
course_runs = course_runs.prefetch_related(*prefetch_fields)
serializer = serializers.FlattenedCourseRunWithCourseSerializer( serializer = serializers.FlattenedCourseRunWithCourseSerializer(
course_runs, many=True, context={'request': request} course_runs, many=True, context={'request': request}
...@@ -289,7 +297,7 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -289,7 +297,7 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
return qs return qs
else: else:
queryset = super(CourseRunViewSet, self).get_queryset().filter(course__partner=partner) queryset = super(CourseRunViewSet, self).get_queryset().filter(course__partner=partner)
queryset = queryset.select_related('course', 'language', 'video') queryset = queryset.select_related(*SELECT_RELATED_FIELDS['course_run'])
queryset = queryset.prefetch_related(*PREFETCH_FIELDS['course_run']) queryset = queryset.prefetch_related(*PREFETCH_FIELDS['course_run'])
return queryset return queryset
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment