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.