Django Сортировать по значению обратного внешнего ключа отбрасывает элементы без связей - PullRequest
0 голосов
/ 24 марта 2020

Контекст

Я пытаюсь отсортировать список объектов на основе значения обратной связи по внешнему ключу. У меня есть частичное решение, но используемый фильтр создает внутреннее соединение, которое удаляет объекты без связи.

В результате я хочу создать отсортированный набор запросов со всеми объектами, угрожая "нулевым" обратным отношениям, как если бы связанное значение было нулевым .

Модели

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']
>>>

1 Ответ

1 голос
/ 25 марта 2020

Я бы использовал выражение подзапроса в аннотации.

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.
...