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)

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.