Django update queryset mit Anmerkung
Ich aktualisieren möchten, werden alle Zeilen in queryset durch die Verwendung von annotierten Wert.
Habe ich 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 will awoid diesem code:
for relation in Relation.objects.annotate(total_rating=Sum('sign_relations__rating')):
relation.rating = relation.total_rating or 0
relation.save()
Und aktualisieren in eine SQL-Anfrage mit etwas wie dieses:
Relation.objects.update(rating=Sum('sign_relations__rating'))
Nicht funktioniert:
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, zu verwenden Djangos ORM für diesen Zweck? Es gibt keine info über die Verwendung update() und annotate() zusammen in docs.
- Ich bin mir nicht sicher, ob dies auch möglich, in reinem SQL? Wenn Sie eine AKTUALISIERUNG in SQL, ich glaube nicht, dass es möglich ist, mit anderen Tabellen.
- Ja, es ist möglich - über Unterabfragen, ex.
UPDATE t1 SET a = (SELECT SUM(c) from t2 where t2.b=t1.b)
; - Was sind Sie wirklich versuchen, zu tun? Sind Sie versuchen, die Summe aller Werte der Spalte 'Bewertung' auf 'SigningRelation' Tabelle und speichern Sie es dann als eine neue Zeile in der "Verhältnis" - Tabelle?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Für Django 1.11+ können Sie Unterabfrage:
Dieser code erzeugt den selben SQL-code, vorgeschlagen von Tomasz Jakub Rup aber mit keinen Gebrauch von RawSQL Ausdruck (Django-Dokumentation warne Sie über die Verwendung es, weil SQL-injection).
Update
Ich einen Artikel veröffentlicht, basierend auf dieser Antwort mit mehr in-depth-Erklärungen:
"Aktualisieren Django queryset mit Anmerkung und Unterabfrage" auf paulox.net
update
mit einemSubquery
ist super cool, danke für den Hinweis auf dieses Muster!.values_list('total_rating', flat=True)
extrahieren Sie den Wert direkt ein, anstatt die[:1]
was ich nicht auf Anhieb verstehen.Relation.objects.update(rating=SubqueryAvg('sign_relations__rating'))
UPDATE
Erklärung nicht unterstützenGROUP BY
. Siehe z.B. PostgreSQL-Docs, SQLite-Docs.Müssen Sie etwas wie dieses:
Entspricht in DjangoORM:
oder:
.filter(relation=F('relation__pk'))
gleichzusetzenWHERE (relation.id = relation.id)
und daher nicht alles mitmachen?.extra()
Klausel ist gold!Workaround für postgres:
Definieren Sie Ihre eigenen benutzerdefinierten Objekte-manager:
Dann in deinem code:
Hinzufügen args/kwargs anpassen, was dieser manager gibt.
Kann man wirklich nicht tun. Werfen Sie einen Blick auf der code für
update
und Folgen Sie durch für einige feine Lektüre.Ehrlich, was ist falsch mit der Platzierung etwas wie dies in einer Manager-definition? Diese 3 Zeilen, die Sie nicht wollen, um in Ihre Sicht zu einem manager, Anruf, dass manager wie nötig. Zusätzlich, Sie tun viel weniger "Magie", und wenn der nächste Entwickler sieht in Ihrem code, Sie müssen nicht greifen, um ein paar WTF ' s .. 🙂
Außerdem war ich neugierig und es sieht aus wie Sie verwenden können, SQL Join mit UPDATE-Anweisungen aber es ist eine klassische SQL-Hack .. Also wenn Sie so geneigt sind, können Sie Djangos raw-SQL-Funktionalität für das 😉
Wenn Sie möchten, um zu vermeiden, viele Aufrufe an die Datenbank, die Sie verwenden sollten
transaction.atomic
.Lesen Sie mehr auf Django-Dokumentation: https://docs.djangoproject.com/en/1.9/topics/db/transactions/#controlling-transactions-explicitly
transaction.atomic
verringert nicht die Anzahl der Aufrufe, die es nur erzwingt, dass die gesamte Transaktion entweder erfolgreich ist oder komplett ausfällt, ohne irgendetwas zu verändern.