2013-04-03 26 views
5

Tôi đang xây dựng một trang web Django để thảo luận. Người dùng có thể tham gia thảo luận và cũng có thể bỏ phiếu tán thành cho các cuộc thảo luận và thông điệp trong các cuộc thảo luận. Mô hình dữ liệu được đơn giản hóa như sau:Django: đặt hàng một queryset bằng tổng các trường được chú thích?

class Discussion: 
    name = models.CharField(max_length=255) 

class Message: 
    owner = models.ForeignKey(User, related_name='messages') 
    body = models.TextField() 
    discussion = models.ForeignKey(Discussion, related_name='messages') 

class MessageApprovalVote: 
    owner = models.ForeignKey(User, related_name='message_approval_votes') 
    message = models.ForeignKey(Message, related_name='approval_votes') 

class DiscussionApprovalVote: 
    owner = models.ForeignKey(User, related_name='discussion_approval_votes') 
    discussion = models.ForeignKey(Discussion, related_name='approval_votes') 

Tôi muốn chọn 20 cuộc thảo luận "hoạt động tích cực nhất", có nghĩa là sắp xếp theo tổng số lượng tin nhắn, tổng số phiếu bầu phê duyệt tin nhắn và số lượng chính thảo luận bỏ phiếu cho cuộc thảo luận đó, hoặc (trong giả):

# Doesn't work 
Discussion.objects. 
    order_by(Count('messages') + 
      Count('approval_votes') + 
      Count('messages__approval_votes')) 

Sử dụng chú thích, tôi có thể tính toán tổng số của mỗi yếu tố ba điểm:

scores = Discussion.objects.annotate(
    total_messages=Count('messages', distinct=True), 
    total_discussion_approval_votes=Count('approval_votes', distinct=True), 
    total_message_approval_votes=Count('messages__approval_votes', distinct=True)) 

tôi sau đó t hought Tôi đã vào một cái gì đó khi tôi tìm thấy extra phương pháp:

total_scores = scores.extra(
    select={ 
     'score_total': 'total_messages + total_discussion_approval_votes + total_message_approval_votes' 
    } 
) 

và sau đó sẽ có thể làm:

final_answer = total_scores.order_by('-score_total')[:20] 

nhưng cuộc gọi extra đưa ra một DatabaseError:

DatabaseError: column "total_messages" does not exist 
LINE 1: SELECT (total_votes + total_messages + total_persuasions) AS... 

và do đó tôi đã thất bại. Phương thức extra có không tham chiếu các trường annotate d? Có một cách khác để làm những gì tôi đang cố gắng để làm, ngắn của việc sử dụng một truy vấn sql nguyên? Tôi đang sử dụng Postgres nếu điều đó tạo nên sự khác biệt.

Mọi thông tin chi tiết sẽ được đánh giá cao!

+0

https : //docs.djangoproject.com/en/dev/topics/db/aggregation/#aggregating-annotations Điều này không đủ hữu ích? – karthikr

+0

Tổng hợp không có vẻ như nó sẽ giúp ích trong trường hợp này bởi vì tôi cần lấy lại một truy vấn của các đối tượng Thảo luận, không chỉ là số đếm của từng yếu tố chấm điểm. – davidscolgan

Trả lời

4

Tôi không nghĩ rằng điều này là có thể trong một truy vấn SQL cấp cao nhất. Giá trị score_total phụ thuộc vào ba kết quả tổng hợp, nhưng bạn đang yêu cầu tất cả chúng được tính cùng một lúc.

Trong SQL thẳng, bạn có thể làm điều này với truy vấn phụ, nhưng tôi không chắc chắn cách chèn nó vào Django. Sau khi thiết lập một ứng dụng Django đơn giản với mô hình của bạn, truy vấn sau đây dường như làm các trick chống lại một cơ sở dữ liệu SQLite:

SELECT id, name, 
    total_messages, total_discussion_approval_votes, total_message_approval_votes, 
    (total_messages + 
    total_discussion_approval_votes + 
    total_message_approval_votes) as score_total 
FROM 
    (SELECT 
    discussion.id, 
    discussion.name, 
    COUNT(DISTINCT discussionapprovalvote.id) AS total_discussion_approval_votes, 
    COUNT(DISTINCT messageapprovalvote.id) AS total_message_approval_votes, 
    COUNT(DISTINCT message.id) AS total_messages 
    FROM discussion 
    LEFT OUTER JOIN discussionapprovalvote 
     ON (discussion.id = discussionapprovalvote.discussion_id) 
    LEFT OUTER JOIN message 
     ON (discussion.id = message.discussion_id) 
    LEFT OUTER JOIN messageapprovalvote 
     ON (message.id = messageapprovalvote.message_id) 
    GROUP BY discussion.id, discussion.name) 

ORDER BY score_total DESC 
LIMIT 20; 
+2

Thật dễ dàng để chạy điều này từ Django bằng cách sử dụng [truy vấn SQL thô] (https://docs.djangoproject.com/en/dev/topics/db/sql/): 'Discussion.objects.raw ('' 'SELECT. .. etc ... '' ') ' –

0

Trên thực tế có một cách bằng cách sử dụng một chú thích thêm với F expressions:

Discussion.objects.annotate(
    total_messages=Count('messages', distinct=True), 
    total_discussion_approval_votes=Count('approval_votes', distinct=True), 
    total_message_approval_votes=Count('messages__approval_votes', distinct=True)), 
    total_score=F('total_messages') + F('total_discussion_approval_votes') + F('total_message_approval_votes') 
).order_by('total_score')