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.