Django 在搜索表单中同时注释 Sum 和 Min/Max

Django Annotate With Sum And Min/Max At The Same Time In Search Form

我有一个课程页面,它是课程搜索表单的搜索结果。在下面的当前代码中,它目前允许用户输入最小和最大视图以及包含该课程 "views" 标准的 return 课程。

本应在课程 "views" field/column

中,但在课程 "views" field/column 中错误地执行了此操作

我想注释(或子查询?)所有课程视图的总和,但要确保用户能够找到课程视图符合其 min/max 搜索条件的所有课程?换句话说,让用户能够通过搜索表单过滤和搜索具有 X 最小和 Y 最大视图之间的课程视图的课程以及 return 所有具有该标准的课程。或者仅按仅具有 X 最小观看次数或仅 Y 最大观看次数的课程进行搜索。

获取课程中所有课程视图总和的有效注释:

Course.objects.annotate(foobar=Sum("lesson__views")).order_by("foobar")

尝试过:

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")

错误: django.core.exceptions.FieldError:无法计算 Min('Sum'):'Sum' 是一个聚合

代码:

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)

如果我没理解错的话,你需要这个:

qs = Course.objects.annotate(Min("lesson__views"), Max("lesson_views")).\
    aggregate(Sum("lesson__views__min"), Sum("lesson__views__max"))

该查询集应包含两个字段:lesson__views__min__sumlesson__views__max__sum,您可以使用它们进行过滤。


如果您的 Lesson 模型中已经有 views,您可以从 Course 模型中过滤 Lessons

qs = Course.objects.filter(lesson__views__lt=top_limit, 
                           lesson__views__gt=bottom__limit).\
                          distinct()

这会根据 个人 的观看次数选择 Lesson,即回答 "Give me all Courses which includes at least one Lesson with its views between X and Y"。

如果您需要聚合所有 Lesson 视图然后过滤:

qs = Course.objects.annotate(Sum("lesson__views")).filter(
    lesson__views__sum__lt=top_limit,
    lesson__views__sum__gt=bottom_limit)

这回答了问题 "Give me all Courses which the Sum of all the views of its Lessons combined are between X and Y"。