2010-09-06 12 views
16

Ich möchte alle Zeilen in Abfragegruppe aktualisieren, indem Sie einen annotierten Wert verwenden.Django-Aktualisierungsabfragegruppe mit Anmerkung

Ich habe eine einfache Modelle:

class Relation(models.Model): 
    rating = models.IntegerField(default=0) 

class SignRelation(models.Model): 
    relation = models.ForeignKey(Relation, related_name='sign_relations') 
    rating = models.IntegerField(default=0) 

Und ich möchte diesen Code awoid:

for relation in Relation.objects.annotate(total_rating=Sum('sign_relations__rating')): 
    relation.rating = relation.total_rating or 0 
    relation.save() 

Und aktualisiere in eine SQL-Anfrage durch so etwas wie dies mit:

Relation.objects.update(rating=Sum('sign_relations__rating')) 

Funktioniert nicht:

TypeError: int() argument must be a string or a number, not 'Sum' 

oder

Relation.objects.annotate(total_rating=Sum('sign_relations__rating')).update(rating=F('total_rating')) 

auch nicht funktioniert:

DatabaseError: missing FROM-clause entry for table "relations_signrelation" 
LINE 1: UPDATE "relations_relation" SET "rating" = SUM("relations_si... 

Ist es möglich, Django ORM für diesen Zweck zu benutzen? Es gibt keine Informationen über die Verwendung von update() und annotieren() zusammen in docs.

+1

ich dies sogar möglich, in reiner SQL nicht sicher bin? Wenn ich ein UPDATE in SQL mache, glaube ich nicht, dass es möglich ist, anderen Tabellen beizutreten. – user27478

+1

Ja, es ist möglich - über Unterabfragen, z. UPDATE t1 SET a = (SELECT SUM (c) von t2 mit t2.b = t1.b) '; –

+0

Was versuchst du wirklich? Versuchen Sie, alle Werte der Spalte "Bewertung" in der Tabelle "SigningRelation" zusammenzufassen und dann als neue Zeile in der Tabelle "Relation" zu speichern? – phourxx

Antwort

-2

Sie können das wirklich nicht tun. Werfen Sie einen Blick auf the code für update und follow it through für einige feine Lesung.

Ehrlich, was ist falsch daran, so etwas in eine Manager-Definition zu platzieren? Setzen Sie die drei Zeilen, die Sie nicht in Ihre Ansicht einfügen möchten, bei Bedarf in den Manager. Darüber hinaus machst du viel weniger "Magie" und wenn der nächste Entwickler deinen Code betrachtet, werden sie nicht auf ein paar WTF's zurückgreifen müssen. :)

Auch ich war neugierig und es sieht wie du aus verwenden SQL Join with UPDATE statements können, aber es gibt einige klassische SQL hackery .. also, wenn Sie so geneigt sind, können Sie Djangos raw SQL-Funktionalität für die Verwendung;)

+4

"Ehrlich, was ist falsch daran, so etwas in eine Manager-Definition zu stellen?" Das könnte eine * Menge * von Anfragen an die DB sein. Er hat speziell "eine SQL-Anfrage" angefordert. Außerdem denke ich, dass die Beispiele, die er ausprobiert hat, leicht zu lesen sind und nicht wie Magie. –

+1

Vor kurzem musste ich eine SQL-Abfrage manuell schreiben, weil richtig geschrieben dauerte es 200ms zu führen, während bei der Verwendung eines Update-Look musste ich 30 Minuten warten. Das ist der Grund, warum. – Xowap

4

UPDATE Anweisung nicht GROUP BY unterstützt. Siehe z.B. PostgreSQL Docs, SQLite Docs.

Sie müssen someting wie folgt aus:

UPDATE relation 
SET rating = (SELECT SUM(rating) 
       FROM sign_relation 
       WHERE relation_id = relation.id) 

Equivalent in DjangoORM:

from django.db.models.expressions import RawSQL 

Relation.objects.all(). \ 
    update(rating=RawSQL('SELECT SUM(rating) FROM signrelation WHERE relation_id = relation.id', [])) 

oder:

from django.db.models import F, Sum 
from django.db.models.expressions import RawSQL 

Relation.objects.all(). \ 
    update(rating=RawSQL(SignRelation.objects. \ 
         extra(where=['relation_id = relation.id']). \ 
         values('relation'). \ 
         annotate(sum_rating=Sum('rating')). \ 
         values('sum_rating').query, [])) 
+0

Konnten Sie Ihre letzte Empfehlung ausführen? Ist '.filter (relation = F ('relation__pk'))' gleich 'WHERE (relation.id = relation.id)' und deshalb nichts? – jnns

+0

Danke für die Aktualisierung. Diese '.extra()' Klausel ist Gold! – jnns

-1

Sie können Ihre eigenen benutzerdefinierten Objekte Manager definieren:

class RelationManager(models.Manager): 
    def annotated(self,*args,*kwargs): 
     queryset = super(RelationManager,self).get_queryset() 
     for obj in queryset: 
       obj.rating = ... do something ... 
     return queryset 

class Relations(models.Model): 
    rating = models.IntegerField(default=0) 
    rating_objects = RelationManager() 

Dann im Code:

q = Realation.rating_objects.annotated() 

hinzufügen args/kwargs was anpassen Dieser Manager kehrt zurück.

0

Abhilfe für Postgres:

with connection.cursor() as cursor: 
    sql, params = qs.query.sql_with_params() 
    cursor.execute(""" 
     WITH qs AS ({}) 
     UPDATE foo SET bar = qs.bar 
     FROM qs WHERE qs.id = foo.id 
    """.format(sql), params)