Django nhận QuerySet từ mảng id theo thứ tự cụ thể


83

rất nhanh cho bạn:

Tôi có một danh sách các id mà tôi muốn sử dụng để trả về một QuerySet (hoặc mảng nếu cần), nhưng tôi muốn duy trì thứ tự đó.

Cảm ơn

Câu trả lời:


73

Tôi không nghĩ rằng bạn có thể thực thi thứ tự cụ thể đó trên cấp cơ sở dữ liệu, vì vậy thay vào đó bạn cần thực hiện nó trong 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]

Điều này xây dựng một từ điển của các đối tượng với id của chúng là khóa, vì vậy chúng có thể được truy xuất dễ dàng khi xây dựng danh sách đã sắp xếp.


Tôi đã hy vọng rằng đây không phải là trường hợp :( thanks cho mã sạch!
neolaser

3
Chỉ cần cẩn thận với các truy vấn lớn vì bạn sẽ lưu kết quả vào bộ nhớ khi thực hiện việc này.
Jj.

12
Có thể sử dụng phương thức querysets in_bulk () thay vì tự mình xây dựng từ điển?
Matt Austin

Vấn đề với điều này là nếu một đối tượng trong id_list không tồn tại, nó sẽ tạo ra một lỗi.
madprops

Tại sao không sử dụng đọc hiểu chính tả?
wieczorek1990

180

Kể từ Django 1.8, bạn có thể làm:

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
Câu trả lời hay nhất vì nó thực sự trả về một bộ truy vấn
Teebes

1
Tôi vẫn nghĩ rằng django Case Whenđược đánh giá thấp!
Babu

Tôi sẽ sử dụng distinct()với mệnh đề order_by case when nhưng gặp lỗi. bất kỳ hỗ trợ, xin vui lòng.
elquimista

SELECT DISTINCT ON expressions must match initial ORDER BY expressions- đây là thông báo lỗi
elquimista

Tôi thực sự khuyên bạn đây là câu trả lời tốt nhất!
Tony

28

Nếu bạn muốn thực hiện việc này bằng cách sử dụng in_bulk, bạn thực sự cần kết hợp hai câu trả lời ở trên:

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

Nếu không, kết quả sẽ là một từ điển hơn là một danh sách được sắp xếp cụ thể.


1
Đây không phải là một câu trả lời tốt hơn.
Tony

25

Đây là một cách để làm điều đó ở cấp cơ sở dữ liệu. Sao chép dán từ: blog.mathieu-leplatre.info :

MySQL :

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

Tương tự với 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;

Tương tự với 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',))

Chà. Thật mãnh liệt. Cảm ơn!
Dan Gayle

Cảm ơn vì câu trả lời này.
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
Vui lòng thêm một số văn bản giải thích cách thức hoạt động và lý do tại sao nó sẽ giải quyết vấn đề của OP. Giúp người khác hiểu.
APC

Câu trả lời tốt nhất. Cảm ơn!
webjunkie

Hoạt động tốt, có thể sử dụng thêm thông tin mặc dù
xtrinch

điều này sẽ chỉ hoạt động cho danh sách id đã đặt hàng. Bạn có thể xác nhận xem nó sẽ hoạt động theo trình tự nào không ... có vẻ như không đúng với tôi.
Doogle
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.