Django:过滤通过ForeignKey链接的多个模型?

Jos*_*nel 5 django django-models django-queryset

我想为以下值和模型创建一个filter-sort mixin:

class Course(models.Model):
    title = models.CharField(max_length=70)
    description = models.TextField()
    max_students = models.IntegerField()
    min_students = models.IntegerField()
    is_live = models.BooleanField(default=False)
    is_deleted = models.BooleanField(default=False)
    teacher = models.ForeignKey(User)

class Session(models.Model):
    course = models.ForeignKey(Course)
    title = models.CharField(max_length=50)
    description = models.TextField(max_length=1000, default='')
    date_from = models.DateField()
    date_to = models.DateField()
    time_from = models.TimeField()
    time_to = models.TimeField()

class CourseSignup(models.Model):
    course = models.ForeignKey(Course)
    student = models.ForeignKey(User)
    enrollment_date = models.DateTimeField(auto_now=True)

class TeacherRating(models.Model):
    course = models.ForeignKey(Course)
    teacher = models.ForeignKey(User)
    rated_by = models.ForeignKey(User)
    rating = models.IntegerField(default=0)
    comment = models.CharField(max_length=300, default='')
Run Code Online (Sandbox Code Playgroud)
  • 课程可以是'离散数学1'
  • 课程是与课程相关的个别课程(例如1.简介,2.第I章,3期末考试等)与日期/时间相结合
  • CourseSignup是学生的"注册"
  • TeacherRating记录学生对教师的评分(课程完成后)

我想实现以下功能

  • 按日期(最早的Session.date_from),Course.Name排序(asc,desc)
  • 过滤条件:日期(最早的Session.date_from和最后一次Session.date_to),平均TeacherRating(例如最小值= 3),CourseSignups(例如最少5个用户注册)(这些选项通过GET参数传递,例如sort = date_ascending&f_min_date = 12年10月10日&...)

你会如何为它创建一个函数?

我试过用了

  • 非规范化(只需在课程中为所需的过滤器/排序标准添加一个字段,并在发生变化时更新它),但我对它不是很满意(例如,每次TeacherRating后需要大量更新).
  • ForeignKey查询(Course.objects.filter(session__date_from = xxx)),但我可能会在以后遇到性能问题.

谢谢你的任何tipp!

Fur*_*tor 8

除了使用Q对象进行高级AND/OR查询之外,还要熟悉反向查找.

当Django为外键关系创建反向查找时.在您的情况下,您可以获得属于课程的所有会话,两种方式之一,每种方式都可以过滤.

c = Course.objects.get(id=1)
sessions = Session.objects.filter(course__id=c.id) # First way, forward lookup.
sessions = c.session_set.all() # Second way using the reverse lookup session_set added to Course object.
Run Code Online (Sandbox Code Playgroud)

您还需要熟悉annotate()并且aggregate(),这些允许您计算字段并对结果进行排序/过滤.例如,Count,Sum,Avg,Min,Max等.

courses_with_at_least_five_students = Course.objects.annotate(
    num_students=Count('coursesignup_set__all')
).order_by(
    '-num_students'
).filter(
    num_students__gte=5
)


course_earliest_session_within_last_240_days_with_avg_teacher_rating_below_4 = Course.objects.annotate(
    min_session_date_from = Min('session_set__all')
).annotate(
    avg_teacher_rating = Avg('teacherrating_set__all')
).order_by(
    'min_session_date_from',
    '-avg_teacher_rating'
).filter(
    min_session_date_from__gte=datetime.now() - datetime.timedelta(days=240)
    avg_teacher_rating__lte=4
)
Run Code Online (Sandbox Code Playgroud)

Q用于允许您在查询中进行逻辑AND和逻辑OR.