2013-04-03 10 views
5

Buduję stronę Django do dyskusji. Użytkownicy mogą brać udział w dyskusjach, a także mogą głosować za zatwierdzeniem dyskusji i wiadomości w dyskusjach. Uproszczony model danych jest następujący:Django: zamówić zapytanie złożone przez sumę pól z adnotacjami?

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

Chcę wybrać 20 najlepszych „najbardziej aktywnych” dyskusje, co oznacza zamawianie przez sumę liczby wiadomości, ogólnej liczby głosów homologacji wiadomość, a liczba zatwierdzenie dyskusja głosów w tej dyskusji, lub (w pseudokodzie)

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

Stosując opisów można obliczyć sumy każdego z trzech czynników punktacji:

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

następnie t hought byłem na coś kiedy znalazłem metodę extra:

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

i będzie wówczas w stanie zrobić:

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

ale wywołanie extra daje DatabaseError:

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

iw ten sposób zostałem udaremniony. Czy metoda extra nie może odnosić się do pól? Czy istnieje inny sposób, aby zrobić to, co próbuję zrobić, a nie za pomocą surowego zapytania sql? Używam Postgreatora, jeśli to robi różnicę.

Wszelkie spostrzeżenia będą mile widziane!

+0

https : //docs.djangoproject.com/en/dev/topics/db/aggregation/#aggregating-annotations Czy to nie wystarczy? – karthikr

+0

Agregat nie wygląda na to, że pomoże w tym przypadku, ponieważ muszę odzyskać zestaw zapytań o obiekty dyskusji, a nie tylko zliczenia każdego współczynnika punktacji. – davidscolgan

Odpowiedz

4

Nie sądzę, że jest to możliwe w pojedynczym zapytaniu SQL najwyższego poziomu. Wartość score_total zależy od trzech zagregowanych wyników, ale chcesz, aby wszystkie zostały obliczone w tym samym czasie.

W prostym SQL, można to zrobić z podzapytaniem, ale nie jestem pewien, jak wprowadzić go do Django. Po skonfigurowaniu prostą aplikację Django ze swoich modeli, następujące zapytanie wydaje rade przeciwko SQLite database:

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

Łatwo jest uruchomić to z Django przy użyciu [surowego zapytania SQL] (https://docs.djangoproject.com/en/dev/topics/db/sql/): 'Discussion.objects.raw ('' 'SELECT. ... itd ... "") " –

0

Faktycznie istnieje sposób, za pomocą dodatkowego adnotacje z 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') 
Powiązane problemy