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.