Commit cfe0c6ae by Matjaz Gregoric Committed by Matjaz Gregoric

Return distinct list of available courses.

The query that fetches available courses performs a join across the
course table, the course_runs table, and the seats table and returns
a separate copy of the same course for each available run/seat
combination by default.

We have to use distinct() to make sure only a single copy of each
available course is returned.
parent 7d00899c
...@@ -50,7 +50,14 @@ class CourseQuerySet(models.QuerySet): ...@@ -50,7 +50,14 @@ class CourseQuerySet(models.QuerySet):
# runs is published while the other is not. If you used exclude(), the Course # runs is published while the other is not. If you used exclude(), the Course
# would be dropped from the queryset even though it has one run which matches # would be dropped from the queryset even though it has one run which matches
# our availability criteria. # our availability criteria.
return self.filter(enrollable & not_ended & marketable) query = self.filter(enrollable & not_ended & marketable)
# By itself, the query above performs a join across several tables and would return
# a copy of the same course multiple times (a separate copy for each available
# seat in each available run).
# We use distinct() to make sure it only returns a single copy of each available
# course.
return query.distinct()
class CourseRunQuerySet(models.QuerySet): class CourseRunQuerySet(models.QuerySet):
......
...@@ -30,14 +30,24 @@ class CourseQuerySetTests(TestCase): ...@@ -30,14 +30,24 @@ class CourseQuerySetTests(TestCase):
if state in self.available_states: if state in self.available_states:
course = course_run.course course = course_run.course
# This course is available, so should be returned by the
# available() query.
assert list(Course.objects.available()) == [course]
# This run has no seats, but we still expect its parent course # This run has no seats, but we still expect its parent course
# to be included. # to be included.
CourseRunFactory(course=course) CourseRunFactory(course=course)
assert list(Course.objects.available()) == [course]
assert set(Course.objects.available()) == {course} # Generate another course run with available seats.
# Only one instance of the course should be included in the result.
other_course_run = CourseRunFactory(course=course)
for function in state:
function(other_course_run)
other_course_run.save()
assert list(Course.objects.available()) == [course]
else: else:
assert set(Course.objects.available()) == set() assert list(Course.objects.available()) == []
@ddt.ddt @ddt.ddt
......
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