【问题标题】:Django Order By on Reverse Foreign Key value drops items without relationshipsDjango Order By on Reverse Foreign Key value 删除没有关系的项目
【发布时间】:2020-03-24 18:35:55
【问题描述】:

上下文

我正在尝试根据反向外键关系的值对对象列表进行排序。 我有部分解决方案,但使用的过滤器会创建一个内部连接,该连接会删除没有关系的对象。

我想用所有对象创建排序查询集的结果,威胁“空”反向关系,就好像相关值为空一样。

型号

class Student(models.Model):
    name = models.CharField(max_length=128)


class Subject(models.Model):
    title = models.CharField(max_length=128)

class Grade(models.Model):
    student = models.ForeignKey("Student", related_name="grades", on_delete=models.CASCADE)
    subject = models.ForeignKey("Subject", related_name="grades", on_delete=models.CASCADE)
    value = models.IntegerField()

夹具

+------+------------------------+
| name | subject  | grade_value |
+------+----------+-------------+
| beth | math     | 100         |
| beth | history  | 100         |
| mark | math     | 90          |
| mark | history  | 90          |
| mike | history  | 80          |
+------+----------+-------------+

期望的结果

当按学生的“历史”成绩对学生进行排序时,我想得到[ beth (100), mark (90), mike (80) ]

现在假设mark 在家生病,错过了math 考试。

当按学生的“数学”成绩对学生进行排序时,我想得到[ beth (100), mark (90), mike (null) ]

相反,对于按数学成绩排序的返回[ beth (100), mark (90) ]

尝试的解决方案

我知道我可以获取所有结果并重新加入结果,但如果可能的话,我正在寻找一种高效的 ORM 查询集解决方案。

>>> Student.objects.filter(grades__subject__title="history").order_by("grades__value")
<Student: Beth>, <Student: Mark>, <Student: Mike>

>>> Student.objects.filter(grades__subject__title="math").order_by("grades__value")
<Student: Beth>, <Student: Mark>

更新

我尝试按照建议使用联合,但我不确定它为什么会导致相关查找失败:

>>> Student.objects.filter(grades__subject__title="math")\
     .union(Student.objects.all())\
     .order_by("grades__value")

...
django.core.exceptions.FieldError: Cannot resolve keyword 'value' into field. Choices are: grades, id, name

解决方案

下面使用@schillingt 的解决方案进行验证。

grades = Grade.objects.filter(
    student_id=OuterRef('id'), subject__title="math"
)
students = Student.objects.annotate(
    subject_grade=Subquery(grades.values('value')[:1])
)
>>> [f"{s.name}: {s.subject_grade}" for s in students.order_by("subject_grade")]
['mike: None', 'mark: 90', 'beth: 100']
>>>
>>> [f"{s.name}: {s.subject_grade}" for s in students.order_by("-subject_grade")]
['beth: 100', 'mark: 90', 'mike: None']
>>>

【问题讨论】:

  • 问题不是order_by,而是filter(grades__subject__title="math")
  • 您可以使用联合将没有相关成绩的学生连接到结果的末尾吗? docs.djangoproject.com/en/3.0/ref/models/querysets/#union
  • @IainShelvington “问题不在于 order_by,而是过滤器”我明白这一点,但我需要在排序之前进行过滤,否则您不仅要按所选主题的所有值排序。我将更新问题以澄清
  • @IainShelvington 我尝试了一个联合,但它使查询无序。我会更新以反映这一点
  • 您的 order_by 中缺少下划线

标签: python django django-models django-orm


【解决方案1】:

我会在注释中使用Subquery expression

from django.db.models import Subquery, OuterRef
subject = "math"
grades = Grade.objects.filter(
    student_id=OuterRef('id'),
    subject__title=subject,
).order_by('value')
students = Student.objects.annotate(
    annotated_grade=Subquery(grades.values('value')[:1]),
)
print(students.first().annotated_grade) # The first student's highest math grade.

【讨论】:

  • 小错字更正:grades.value应该是grades.values
猜你喜欢
  • 1970-01-01
  • 2022-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-27
  • 2021-06-29
  • 2016-07-24
  • 1970-01-01
相关资源
最近更新 更多