@Mekalure

Django ORM: Как отсортировать по prefetched_set?

Допустим есть две модели: Person и Numbers (ForeignKey на Person, число и еще поля)

Я получаю всех персон вместе с номерами. Но номера дополнительно фильтруются так, что в numbers_set будет либо одно значение, либо никакого. В самом numbers_set может быть несколько значений, но мне нужно конкретно одно или ничего.
persons = Person.objects.all().prefecth_related(Prefecth('numbers_set', queryset=<numbers_filter>))

Проблема в сортировке. Мне надо отсортировать Person по значению номера, но сортировка идет по первому встреченному номеру вообще, а не тем которые предзагружены в Prefetch

А корень проблемы в том, что мне нужны разные сортировки по полям Person и по номерам. По полям сортирую нормально при помощи order_by, а по номерам приходится через sorted(person, key=), что не очень удобно и правильно.

Можно как-то средствами ORM отсортировать как я хочу? order_by принимает QueryExpressions, но я пока не могу сообразить что ему конкретно передать
  • Вопрос задан
  • 434 просмотра
Решения вопроса 1
werevolff
@werevolff
А то, что ты в prefetch получаешь, нельзя как-нибудь прогнать через annotation? А потом уже с аннотации сортировать?

А вообще, может использовать аргумент to_attr, чтобы переименовать атрибут?

class Prefetch(lookup, queryset=None, to_attr=None)
The to_attr argument sets the result of the prefetch operation to a custom attribute.

Я так полагаю, numbers_set может ссылаться на существующее поле объекта.

Решение найдено через FilteredRelation(). Mekalure объяснение: Собственно, Prefetch() метод не создаёт атрибут, который доступен в order_by. Я прогнал у себя: этот атрибут не доступен и в annotate. Только в результате выборки. Поэтому, отсортировать по нему не выйдет. FilteredRelation() объявляется внутри annotate и создаёт QuerySet, доступный в order_by и последующей фильтрации. Но, увы, этот QuerySet не будет доступен в результатах выборки. Подозреваю, что Джанга умеет связывать два QuerySet только через prefetch_related, но не через annotate. В любом случае, эта функция делает нужные данные доступными при сортировке.

Получается:

qs1 = Model1.objects.filter(**kwargs)
qs2 = Model2.objects.annotate(order=FilteredRelation('lookup', condition=Q(**kwarg))).prefetch_related(Prefetch('lookup', queryset=qs1)).order_by('order')
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы