Django pobiera QuerySet z tablicy identyfikatorów w określonej kolejności


84

oto szybki dla ciebie:

Mam listę identyfikatorów, których chcę użyć, aby zwrócić QuerySet (lub tablicę, jeśli trzeba), ale chcę zachować tę kolejność.

Dzięki

Odpowiedzi:


72

Nie sądzę, abyś mógł wymusić tę konkretną kolejność na poziomie bazy danych, więc zamiast tego musisz to zrobić w Pythonie.

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]

Tworzy to słownik obiektów z ich identyfikatorem jako kluczem, dzięki czemu można je łatwo odzyskać podczas tworzenia posortowanej listy.


Miałem nadzieję, że tak nie jest :( dzięki za czysty kod!
— neolaser,

3
Po prostu uważaj na duże zapytania, ponieważ podczas wykonywania tej czynności będziesz przechowywać wynik w pamięci.
— Jj.

14
Może użyj metody querysets in_bulk () zamiast samodzielnego tworzenia słownika?
— Matt Austin,

Problem z tym polega na tym, że jeśli obiekt w id_list nie istnieje, zgłosi błąd.
— madprops

Dlaczego nie użyć dyktowania ze zrozumieniem?
— wieczorek1990

186

Od Django 1.8 możesz:

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
Najlepsza odpowiedź, ponieważ w rzeczywistości zwraca zestaw zapytań
— Teebes

2
Nadal uważam, że django Case Whensą niedoceniane!
— Babu

Zamierzam użyć distinct()z klauzulą ​​order_by, gdy wystąpił błąd. jakieś wsparcie, proszę.
— elquimista

SELECT DISTINCT ON expressions must match initial ORDER BY expressions- tutaj jest komunikat o błędzie
— elquimista

1
To powinna być wybrana odpowiedź.
— Subangkar KrS

28

Jeśli chcesz to zrobić za pomocą in_bulk, w rzeczywistości musisz połączyć dwie powyższe odpowiedzi:

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

W przeciwnym razie wynikiem będzie słownik, a nie specjalnie uporządkowana lista.


1
To nie jest lepsza odpowiedź.
— Tony

26

Oto sposób na zrobienie tego na poziomie bazy danych. Skopiuj wklej z: blog.mathieu-leplatre.info :

MySQL :

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

To samo z 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;

To samo z 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',))

Łał. To jest intensywne. Dzięki!
— Dan Gayle

Dzięki za tę odpowiedź.
— 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
Dodaj tekst wyjaśniający, jak to działa i dlaczego rozwiązałoby to problem PO. Pomóż innym zrozumieć.
— APC

Najlepsza odpowiedź. Dzięki!
— webjunkie

Działa dobrze, ale
— przydałoby się

będzie to działać tylko dla zamówionej listy identyfikatorów. Czy możesz potwierdzić, czy zadziała w dowolnej kolejności ... wydaje mi się to nieprawdą.
— Doogle
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.