I have a page of courses which are search results of a course search form. In the current code below, it currently allows a user to type in the min and max views and return courses that contain that Course "views" criteria.
It incorrectly does this with the Course "views" field/column when it should be doing it on the Lesson "views" field/column
I want to annotate (or subquery?) the sum of all lesson views but make sure a user is able to find all courses with lesson views that fall under their min/max search critera? In other words, to have the user be able to filter and search via the search form by Courses with Lesson views between X min and Y max views and return all the courses that have that criteria. Or just search by courses with X min views only or Y max views only.
Valid annotate that gets the total sum of all lesson views in a course:
Course.objects.annotate(foobar=Sum("lesson__views")).order_by("foobar")
Tried doing:
if min_views_query:
qs = Course.objects.annotate(foobar=Sum(Min("lesson__views"))).order_by("foobar")
if max_views_query:
qs = Course.objects.annotate(foobar=Sum(Max("lesson__views"))).order_by("foobar")
Error: django.core.exceptions.FieldError: Cannot compute Min('Sum'): 'Sum' is an aggregate
Code:
Courses Forms.py:
class CourseForm(forms.Form):
min_views = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off','id':'min_views', 'type':'number', 'min':'0', 'placeholder': '0'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
max_views = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'max_views', 'type':'number', 'min':'0', 'placeholder': '1000000'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
def __init__(self, *args, **kwargs):
super(CourseForm, self).__init__(*args, **kwargs)
self.fields['min_views'].label = "Min Views:"
self.fields['max_views'].label = "Max Views:"
Courses Views.py:
class CourseListView(ListView):
model = Course
def get_queryset(self):
qs = super().get_queryset()
self.form = form = CourseForm(self.request.GET)
if form.is_valid():
min_views_query = self.request.GET.get('min_views')
max_views_query = self.request.GET.get('max_views')
if min_views_query:
qs = qs.filter(views__gte=min_views_query)
if max_views_query:
qs = qs.filter(views__lte=max_views_query)
return qs
Courses Models.py:
class Course(models.Model):
views = models.PositiveIntegerField(default=0)
@property
def total_lesson_views(self):
lessons_dictionary = Lesson.objects.filter(course=self).aggregate(Sum('views'))
return lessons_dictionary['views__sum']
class Lesson(models.Model):
course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True)
views = models.PositiveIntegerField(default=0)