Django получает QuerySet из массива идентификаторов в определенном порядке

84

вот вам быстрый:

У меня есть список идентификаторов, которые я хочу использовать для возврата QuerySet (или массива, если необходимо), но я хочу сохранить этот порядок.

благодаря

неолазер
источник

Ответы:

72

Я не думаю, что вы можете обеспечить соблюдение этого конкретного порядка на уровне базы данных, поэтому вам нужно сделать это на python.

id_list = [1, 5, 7]
objects = Foo.objects.filter(id__in=id_list)

objects = dict([(obj.id, obj) for obj in objects])
sorted_objects = [objects[id] for id in id_list]

Это создает словарь объектов с их идентификатором в качестве ключа, поэтому их можно легко получить при построении отсортированного списка.

Райнер Гереке
источник
Я надеялся, что это не так :( Спасибо за чистый код!
neolaser
3
Просто остерегайтесь огромных запросов, так как при этом вы будете сохранять результат в памяти.
Jj.
14
Может быть, использовать метод querysets in_bulk () вместо того, чтобы создавать словарь самостоятельно?
Мэтт Остин
Проблема в том, что если объект в id_list не существует, он выдаст ошибку.
madprops
Почему бы не использовать понимание диктовки?
wieczorek1990
186

Начиная с Django 1.8, вы можете:

from django.db.models import Case, When

pk_list = [10, 2, 1]
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(pk_list)])
queryset = MyModel.objects.filter(pk__in=pk_list).order_by(preserved)
Soitje
источник
16
Лучший ответ, потому что он действительно возвращает набор
запросов
2
Я до сих пор считаю, что django Case Whenнедооценивают!
Бабу
Я собираюсь использовать distinct()с предложением order_by case when, но получил ошибку. любая поддержка, пожалуйста.
elquimista
SELECT DISTINCT ON expressions must match initial ORDER BY expressions- вот сообщение об ошибке
elquimista
1
Это должен был быть выбранный ответ.
Subangkar KrS 09
28

Если вы хотите сделать это с помощью in_bulk, вам действительно нужно объединить два ответа выше:

id_list = [1, 5, 7]
objects = Foo.objects.in_bulk(id_list)
sorted_objects = [objects[id] for id in id_list]

В противном случае результатом будет словарь, а не специально упорядоченный список.

Рик Вестера
источник
1
Это не лучший ответ.
Тони
26

Вот способ сделать это на уровне базы данных. Скопируйте и вставьте из: blog.mathieu-leplatre.info :

MySQL :

SELECT *
FROM theme
ORDER BY FIELD(`id`, 10, 2, 1);

То же самое с Django:

pk_list = [10, 2, 1]
ordering = 'FIELD(`id`, %s)' % ','.join(str(id) for id in pk_list)
queryset = Theme.objects.filter(pk__in=[pk_list]).extra(
           select={'ordering': ordering}, order_by=('ordering',))

PostgreSQL :

SELECT *
FROM theme
ORDER BY
  CASE
    WHEN id=10 THEN 0
    WHEN id=2 THEN 1
    WHEN id=1 THEN 2
  END;

То же самое с Django:

pk_list = [10, 2, 1]
clauses = ' '.join(['WHEN id=%s THEN %s' % (pk, i) for i, pk in enumerate(pk_list)])
ordering = 'CASE %s END' % clauses
queryset = Theme.objects.filter(pk__in=pk_list).extra(
           select={'ordering': ordering}, order_by=('ordering',))
пользователь
источник
Вау. Это интенсивно. Благодаря!
Дэн Гейл
Спасибо за этот ответ.
Subangkar KrS 09 авг.202020,
9
id_list = [1, 5, 7]
objects = Foo.objects.filter(id__in=id_list)
sorted(objects, key=lambda i: id_list.index(i.pk))
Эндрю Г
источник
1
Добавьте текст, объясняющий, как это работает и почему это решит проблему OP. Помогите другим понять.
APC
Лучший ответ. Благодаря!
webjunkie
Работает хорошо, хотя можно использовать дополнительную информацию
xtrinch
это будет работать только для упорядоченного списка idlist. Можете ли вы подтвердить, будет ли он работать в любой последовательности ... Мне это кажется неправдой.
Doogle