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


84

ось швидкий для вас:

У мене є список ідентифікаторів, які я хочу використовувати для повернення набору запитів (або масиву, якщо це потрібно), але я хочу підтримувати цей порядок.

Дякую

Відповіді:


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
Можливо, скористатися методом наборів запитів in_bulk (), а не будувати словник самостійно?
— Matt Austin

Проблема з цим полягає в тому, що якщо об’єкт у списку id_list не існує, він видасть помилку.
— madprops

Чому б не використовувати розуміння дикту?
— wieczorek1990

187

Починаючи з 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
Найкраща відповідь, оскільки він фактично повертає набір запитів
— Teebes

2
Я все ще думаю, що джанго Case Whenнедооцінені!
— Бабу

Я збираюся використовувати distinct()з case_by case, коли речення, але отримав помилку. будь-яку підтримку, будь ласка.
— elquimista

SELECT DISTINCT ON expressions must match initial ORDER BY expressions- ось повідомлення про помилку
— elquimista

1
Це мала бути обрана відповідь.
— Subangkar KrS

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',))

Ого. Це напружено. Дякую!
— Dan Gayle,

Дякую за цю відповідь.
— Subangkar KrS

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
Будь ласка, додайте текст, який пояснює, як це працює, і чому це може вирішити проблему ОП. Допоможіть іншим зрозуміти.
— APC

Краща відповідь. Дякую!
— webjunkie

Працює добре, хоча міг би використати додаткову інформацію
— xtrinch

це буде працювати лише для замовленого ідентифікатора. Чи можете ви підтвердити, чи буде це працювати в будь-якій послідовності ... мені здається неправдою.
— Doogle
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.