Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion b2b/views/v0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class ContractPageViewSet(viewsets.ReadOnlyModelViewSet):

def get_queryset(self):
"""Filter to only return active contracts by default."""
return ContractPage.objects.filter(active=True)
return ContractPage.objects.filter(active=True).prefetch_related(
"contract_programs"
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefetching contract_programs here won’t eliminate the N+1 in ContractPageSerializer.get_programs, because that method calls instance.programs (a @property that runs a fresh Program.objects.filter(...) query per contract). To actually reduce queries, prefetch the relationship that the property uses (e.g., contract_programs__program) and/or change the serializer to read program IDs from the prefetched contract_programs items instead of calling instance.programs.

Suggested change
"contract_programs"
"contract_programs",
"contract_programs__program",

Copilot uses AI. Check for mistakes.
)


class Enroll(APIView):
Expand Down
14 changes: 8 additions & 6 deletions courses/views/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,21 +244,23 @@ def get_queryset(self):
if relevant_to:
course = Course.objects.filter(readable_id=relevant_to).first()
if course:
return get_relevant_course_run_qset(course)
return get_relevant_course_run_qset(course).prefetch_related("products")
else:
program = Program.objects.filter(readable_id=relevant_to).first()
return (
get_user_relevant_program_course_run_qset(program)
if program
else Program.objects.none()
)
if program:
return get_user_relevant_program_course_run_qset(
program
).prefetch_related("products")
else:
return CourseRun.objects.none()
else:
return (
CourseRun.objects.select_related("course")
.prefetch_related(
"course__departments",
"course__page",
"enrollment_modes",
"products",
)
.filter(live=True)
)
Expand Down
7 changes: 6 additions & 1 deletion courses/views/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,12 @@ def filter_queryset(self, queryset):

if "courserun_is_enrollable" not in filter_keys:
queryset = queryset.prefetch_related(
Prefetch("courseruns", queryset=CourseRun.objects.order_by("id")),
Prefetch(
"courseruns",
queryset=CourseRun.objects.order_by("id").prefetch_related(
"products"
),
),
)
Comment on lines 339 to 346
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Prefetch("courseruns", queryset=...prefetch_related("products")) is unlikely to help if downstream serialization re-queries instance.courseruns with .order_by(...) / .filter(...) (which bypasses the prefetched cache). In CourseWithCourseRunsSerializer.get_courseruns the related manager is queried again, so products may still be fetched N+1. Consider either (1) moving the products prefetch onto the queryset the serializer actually uses, or (2) using to_attr and having the serializer consume the prefetched list without re-querying.

Copilot uses AI. Check for mistakes.

return queryset
Expand Down