Django erhält ein QuerySet aus einem Array von IDs in einer bestimmten Reihenfolge


84

Hier ist eine schnelle für Sie:

Ich habe eine Liste von IDs, mit denen ich ein QuerySet (oder ggf. ein Array) zurückgeben möchte, aber ich möchte diese Reihenfolge beibehalten.

Vielen Dank

Antworten:


73

Ich glaube nicht, dass Sie diese bestimmte Reihenfolge auf Datenbankebene erzwingen können, also müssen Sie dies stattdessen in Python tun.

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]

Dadurch wird ein Wörterbuch der Objekte mit ihrer ID als Schlüssel erstellt, sodass sie beim Erstellen der sortierten Liste leicht abgerufen werden können.


Ich hatte gehofft, dass dies nicht der Fall war :( Danke für den sauberen Code!
Neolaser

3
Achten Sie nur auf große Abfragen, da Sie das Ergebnis dabei im Speicher speichern.
Jj.

13
Verwenden Sie möglicherweise die Methode quersysets in_bulk (), anstatt das Wörterbuch selbst zu erstellen?
Matt Austin

Das Problem dabei ist, dass wenn ein Objekt in der id_list nicht existiert, es einen Fehler auslöst.
Madprops

Warum nicht das Diktatverständnis verwenden?
wieczorek1990

184

Seit Django 1.8 können Sie Folgendes tun:

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)

15
Beste Antwort, weil es tatsächlich ein Abfrageset zurückgibt
Teebes

2
Ich denke immer noch, dass Djangos Case Whenunterschätzt werden!
Babu

Ich werde distinct()mit order_by case when-Klausel verwenden, habe aber den Fehler erhalten. jede Unterstützung bitte.
Elquimista

SELECT DISTINCT ON expressions must match initial ORDER BY expressions- Hier ist die Fehlermeldung
Elquimista

1
Dies sollte die ausgewählte Antwort gewesen sein.
Subangkar KrS

28

Wenn Sie dies mit in_bulk tun möchten, müssen Sie die beiden obigen Antworten zusammenführen:

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

Andernfalls ist das Ergebnis eher ein Wörterbuch als eine speziell geordnete Liste.


1
Dies ist keine bessere Antwort.
Tony

26

Hier ist eine Möglichkeit, dies auf Datenbankebene zu tun. Kopieren Einfügen von: blog.mathieu-leplatre.info :

MySQL :

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

Gleiches gilt für 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;

Gleiches gilt für 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',))

Beeindruckend. Das ist intensiv. Vielen Dank!
Dan Gayle

Danke für diese Antwort.
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
Bitte fügen Sie einen Text hinzu, der erklärt, wie dies funktioniert und warum es das Problem des OP lösen würde. Helfen Sie anderen zu verstehen.
APC

Beste Antwort. Vielen Dank!
Webjunkie

Funktioniert gut, könnte aber zusätzliche Informationen verwenden
xtrinch

Dies funktioniert nur für die bestellte ID-Liste. Können Sie bestätigen, ob es in irgendeiner Reihenfolge funktioniert ... scheint mir nicht wahr zu sein.
Doogle
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.