Django ORM: Выполнение условного `order_by` [дубликат]

Допустим, у человека есть такая простая модель:

from django.db import models

class Foo(models.Model):
    n = models.IntegerField()

В SQL вы можете выполнить порядок по с условием например,

select * from foo orber by n=7, n=17, n=3, n

Это отсортирует строки сначала по возрастанию, если n равно 7, затем если n равно 14, затем если n равно 3, и, наконец, по возрастанию n.

Как сделать то же самое с Django ORM? Это не рассматривается в их order_by документации.

Вы можете использовать .annotate() для присвоения записям значения custom_order и использовать затем .order_by() для упорядочивания набора запросов на основе этого значения.

Например:

Foo.objects \
.annotate(custom_order=Case( 
    When(n=7, then=Value(0)), 
    When(n=17, then=Value(1)), 
    When(n=3, then=Value(2)),
    default=Value(3),
    output_field=IntegerField()
 ) \
.order_by('custom_order', 'n')

Вы можете работать с общим решением, которое выглядит следующим образом:

from django.db.models import Case, IntegerField, When, Value

items = [7, 17, 3]
Foo.objects.alias(
    n_order=Case(
        *[When(n=item, then=Value(i)) for i, item in enumerate(items)],
        default=Value(len(items)),
        output_field=IntegerField()
    )
).order_by('n_order', 'n')

Таким образом, строится условное выражение цепочки [Django-doc], которое используется первым, и если n не является одним из них, оно вернется к упорядочиванию с n само.

Вернуться на верх