Làm cách nào để CHỌN ĐẾM (*) NHÓM THEO và ĐẶT HÀNG THEO trong Django?


95

Tôi đang sử dụng mô hình giao dịch để theo dõi tất cả các sự kiện diễn ra trong hệ thống

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField() 
    ......

làm cách nào để có được 5 diễn viên hàng đầu trong hệ thống của tôi?

Trong sql về cơ bản nó sẽ là

SELECT actor, COUNT(*) as total 
FROM Transaction 
GROUP BY actor 
ORDER BY total DESC

Câu trả lời:


176

Theo tài liệu, bạn nên sử dụng:

from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')

giá trị (): chỉ định cột nào sẽ được sử dụng để "nhóm theo"

Tài liệu Django:

"Khi mệnh đề giá trị () được sử dụng để ràng buộc các cột được trả về trong tập kết quả, thì phương pháp đánh giá chú thích hơi khác. cho các kết hợp duy nhất của các trường được chỉ định trong mệnh đề giá trị () "

annotate (): chỉ định một thao tác trên các giá trị được nhóm

Tài liệu Django:

Cách thứ hai để tạo các giá trị tóm tắt là tạo một bản tóm tắt độc lập cho từng đối tượng trong QuerySet. Ví dụ: nếu bạn đang truy xuất một danh sách sách, bạn có thể muốn biết có bao nhiêu tác giả đã đóng góp cho mỗi cuốn sách. Mỗi Sách có mối quan hệ nhiều-nhiều với Tác giả; chúng tôi muốn tóm tắt mối quan hệ này cho từng cuốn sách trong QuerySet.

Tóm tắt cho từng đối tượng có thể được tạo bằng cách sử dụng mệnh đề annotate (). Khi một mệnh đề annotate () được chỉ định, mỗi đối tượng trong QuerySet sẽ được chú thích với các giá trị được chỉ định.

Thứ tự theo mệnh đề là tự giải thích.

Tóm lại: bạn nhóm theo, tạo tập hợp các tác giả, thêm chú thích (điều này sẽ thêm một trường bổ sung vào các giá trị được trả về) và cuối cùng, bạn sắp xếp chúng theo giá trị này

Tham khảo https://docs.djangoproject.com/en/dev/topics/db/aggregation/ để biết thêm thông tin chi tiết

Điều cần lưu ý: nếu sử dụng Count, giá trị được truyền cho Count không ảnh hưởng đến tổng hợp, chỉ là tên được đặt cho giá trị cuối cùng. Bộ tổng hợp nhóm theo các kết hợp duy nhất của các giá trị (như đã đề cập ở trên), không theo giá trị được chuyển cho Đếm. Các truy vấn sau đây giống nhau:

Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')

Đối với tôi, nó hoạt động như vậy Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total'), đừng quên nhập Count từ django.db.models. Cảm ơn bạn
Ivancho

3
Điều cần lưu ý: nếu sử dụng Count(và có thể các trình tổng hợp khác), giá trị được chuyển đến Countkhông ảnh hưởng đến việc tổng hợp, chỉ là tên được đặt cho giá trị cuối cùng. Bộ tổng hợp nhóm theo các kết hợp duy nhất của values(như đã đề cập ở trên), không theo giá trị được chuyển đến Count.
kronosapiens

Bạn thậm chí có thể sử dụng điều này cho các tập hợp truy vấn kết quả tìm kiếm postgres để có được các khía cạnh!
yekta

2
@kronosapiens Nó ảnh hưởng đến nó, ít nhất là hiện nay (tôi đang sử dụng Django 2.1.4). Trong ví dụ, totallà tên được đặt và số lượng sử dụng trong sql được COUNT('actor')mà trong trường hợp này không quan trọng, nhưng nếu ví dụ values('x', 'y').annotate(count=Count('x')), bạn sẽ nhận được COUNT(x), không COUNT(*)hay COUNT(x, y), chỉ cần thử nó trong./manage.py shell
timdiels

33

Giống như @Alvaro đã trả lời câu lệnh tương đương trực tiếp của Django GROUP BY:

SELECT actor, COUNT(*) AS total 
FROM Transaction 
GROUP BY actor

là thông qua việc sử dụng values()annotate()các phương pháp như sau:

Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()

Tuy nhiên, cần phải chỉ ra một điều nữa:

Nếu mô hình có một thứ tự mặc định được xác định class Meta, thì .order_by()mệnh đề này là bắt buộc để có kết quả phù hợp. Bạn chỉ không thể bỏ qua nó ngay cả khi không có ý định đặt hàng.

Hơn nữa, đối với một mã chất lượng cao, bạn nên luôn đặt một .order_by()mệnh đề sau annotate(), ngay cả khi không có class Meta: ordering. Cách tiếp cận như vậy sẽ làm cho tuyên bố có thể chứng minh được trong tương lai: nó sẽ hoạt động đúng như dự kiến, bất kể bất kỳ thay đổi nào trong tương lai class Meta: ordering.


Hãy để tôi cung cấp cho bạn một ví dụ. Nếu mô hình có:

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField()

    class Meta:
        ordering = ['id']

Sau đó, cách tiếp cận như vậy WOULDN'T hoạt động:

Transaction.objects.values('actor').annotate(total=Count('actor'))

Đó là bởi vì Django thực hiện bổ sung GROUP BYtrên mọi trường trongclass Meta: ordering

Nếu bạn in truy vấn:

>>> print Transaction.objects.values('actor').annotate(total=Count('actor')).query
  SELECT "Transaction"."actor_id", COUNT("Transaction"."actor_id") AS "total"
  FROM "Transaction"
  GROUP BY "Transaction"."actor_id", "Transaction"."id"

Rõ ràng là tập hợp sẽ KHÔNG hoạt động như dự định và do đó .order_by()điều khoản phải được sử dụng để xóa hành vi này và nhận được kết quả tổng hợp thích hợp.

Xem: Tương tác với thứ tự mặc định hoặc order_by () trong tài liệu Django chính thức.


3
.order_by()đã cứu tôi khỏi orderingMeta.
Babken Vardanyan
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.