7

ディスカッション用の Django サイトを構築しています。ユーザーはディスカッションに参加でき、ディスカッションおよびディスカッション内のメッセージに対して承認の投票を行うこともできます。単純化されたデータ モデルは次のとおりです。

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')

上位 20 の「最も活発な」ディスカッションを選択したいと考えています。これは、メッセージ数、メッセージ承認投票の総数、およびそのディスカッションのディスカッション承認投票数の合計で並べ替えることを意味します。または (擬似コードで):

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

注釈を使用して、3 つのスコア要因それぞれの合計を計算できます。

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))

メソッドを見つけたとき、私は何かに取り組んでいると思いましたextra

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

そして、次のことができます:

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

しかし、extra呼び出しは次のようになりますDatabaseError:

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

したがって、私は失敗しました。メソッドはd フィールドextraを参照できませんか? annotate生のSQLクエリを使用する以外に、私がやろうとしていることを行う別の方法はありますか? それが違いを生む場合、私はPostgresを使用しています。

どんな洞察も大歓迎です!

4

2 に答える 2

4

単一の最上位 SQL クエリでこれが可能だとは思いません。score_total の値は 3 つの集計結果によって異なりますが、それらすべてを同時に計算するように求めています。

ストレート SQL では、サブクエリでこれを行うことができますが、Django に注入する方法がわかりません。モデルで単純な Django アプリをセットアップした後、次のクエリは 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;
于 2013-04-05T01:21:03.980 に答える