Chọn DISTINCT các cột riêng lẻ trong django?


93

Tôi tò mò nếu có bất kỳ cách nào để thực hiện một truy vấn trong Django không phải là " SELECT * FROM..." bên dưới. SELECT DISTINCT columnName FROM ...Thay vào đó, tôi đang cố tạo " ".

Cụ thể, tôi có một mô hình trông giống như:

class ProductOrder(models.Model):
   Product  = models.CharField(max_length=20, promary_key=True)
   Category = models.CharField(max_length=30)
   Rank = models.IntegerField()

trong đó Ranklà một thứ hạng trong a Category. Tôi muốn có thể lặp lại tất cả các Danh mục thực hiện một số thao tác trên mỗi thứ hạng trong danh mục đó.

Trước tiên, tôi muốn nhận danh sách tất cả các danh mục trong hệ thống, sau đó truy vấn tất cả các sản phẩm trong danh mục đó và lặp lại cho đến khi mọi danh mục được xử lý.

Tôi muốn tránh SQL thô, nhưng nếu tôi phải đến đó, điều đó sẽ ổn. Mặc dù tôi chưa bao giờ viết mã SQL thô bằng Django / Python trước đây.

Câu trả lời:


185

Một cách để lấy danh sách các tên cột riêng biệt từ cơ sở dữ liệu là sử dụng distinct() kết hợp với values().

Trong trường hợp của bạn, bạn có thể làm như sau để lấy tên của các danh mục riêng biệt:

q = ProductOrder.objects.values('Category').distinct()
print q.query # See for yourself.

# The query would look something like
# SELECT DISTINCT "app_productorder"."category" FROM "app_productorder"

Có một số điều cần nhớ ở đây. Đầu tiên, điều này sẽ trả về a ValuesQuerySethoạt động khác với a QuerySet. Khi bạn truy cập say, phần tử đầu tiên của q(ở trên) bạn sẽ nhận được một từ điển , KHÔNG PHẢI là một phiên bản của ProductOrder.

Thứ hai, bạn nên đọc lưu ý cảnh báo trong tài liệu về cách sử dụng distinct(). Ví dụ trên sẽ hoạt động nhưng tất cả các kết hợp của distinct()values()có thể không.

Tái bút : bạn nên sử dụng tên viết thường cho các trường trong một mô hình. Trong trường hợp của bạn, điều này có nghĩa là viết lại mô hình của bạn như được hiển thị bên dưới:

class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()

1
Phương pháp này được mô tả dưới đây bây giờ đã có trong django 1.4 và là tốt đẹp nếu bạn cần ProductOrder dụ với lĩnh vực ý thức rõ rệt ;-)
Jonathan Liuti

61

Thực ra nó khá đơn giản nếu bạn đang sử dụng PostgreSQL , chỉ cần sử dụng distinct(columns)( tài liệu ).

Productorder.objects.all().distinct('category')

Lưu ý rằng tính năng này đã được đưa vào Django từ 1.4


@lazerscience, @Manoj Govindan: Tôi xin lỗi, bạn nói đúng. Có vẻ như tôi đã vá Django để thêm tính năng đó. Tôi đã thêm một liên kết đến bản vá
Wolph 4/10/10

3
Điều này hiện có trong Django SVN và sẽ có trong Django 1.4
Will Hardy

14
Lưu ý: trừ khi bạn đang sử dụng PostgreSQL, bạn không thể đưa ra đối số khác biệt (). Tốt nhất hãy gắn bó với giải pháp được chấp nhận ở trên.
Mark Chackerian

trong các bài kiểm tra, điều này can_distinct_on_fieldsdường như chỉ dành cho Postgres
Skylar Saveland

3
cộng với 1, nhưng all()là không cần thiết ở đây
Antony Hatchkins

17

Các câu trả lời khác đều ổn, nhưng điều này rõ ràng hơn một chút, ở chỗ nó chỉ cung cấp các giá trị như bạn sẽ nhận được từ truy vấn DISTINCT, không có bất kỳ lỗi nào từ Django.

>>> set(ProductOrder.objects.values_list('category', flat=True))
{u'category1', u'category2', u'category3', u'category4'}

hoặc là

>>> list(set(ProductOrder.objects.values_list('category', flat=True)))
[u'category1', u'category2', u'category3', u'category4']

Và, nó hoạt động mà không cần PostgreSQL.

Điều này kém hiệu quả hơn so với việc sử dụng .distinction (), giả sử rằng DISTINCT trong cơ sở dữ liệu của bạn nhanh hơn so với python set, nhưng nó rất tốt cho việc bắt chước xung quanh shell.


values_listkhông đưa DISTINCTvào truy vấn sql, vì vậy điều này sẽ mang lại nhiều giá trị nếu có.
mehmet

13

Người dùng đặt hàng theo trường đó, và sau đó thực hiện riêng biệt.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
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.