2010-09-06 73 views
16

我想通過使用帶註釋的值來更新queryset中的所有行。帶有註釋的Django更新查詢集

我有一個簡單的模型:

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) 

我想awoid此代碼:

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

,並通過使用像這樣做一個SQL請求更新:

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

不工作:

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

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

也不起作用:

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

是否有可能使用Django的ORM爲了這個目的?沒有關於在文檔中使用更新()批註()的信息。

+1

我不是純粹的SQL肯定這甚至可能嗎?在SQL中執行UPDATE時,我認爲不可能加入其他表。 – user27478 2010-12-08 13:56:57

+1

是的,這是可能的 - 通過子查詢,例如。 'UPDATE t1 SET a =(從t2.S選擇SUM(c),其中t2.b = t1.b)'; – 2015-02-20 15:03:50

+0

你真的想做什麼? 您是否試圖總結'SigningRelation'表中列'評級'的所有值,然後將其保存爲「關係」表上的新行? – phourxx 2015-12-09 20:19:02

回答

-2

你真的不能這樣做。看看the codeupdatefollow it through一些精美的閱讀。

老實說,在Manager定義中放置類似這樣的東西有什麼問題?把你不想看到的3條線放到經理面前,根據需要給經理打電話。此外,你做的「魔術」要少得多,當下一個開發人員查看你的代碼時,他們將不必訴諸幾個WTF .. :)

另外,我很好奇,它看起來像你可以使用SQL Join with UPDATE statements,但它是一些經典的SQL hackery ..所以如果你這麼傾向,你可以使用Djangos的原始SQL功能;)

+4

「老實說,在管理器定義中放置類似這樣的東西有什麼問題?」 這可能是發送到數據庫的一大堆*請求。他特別要求「一個SQL請求」。另外,我認爲他嘗試的例子很容易閱讀,而不是像魔術一樣。 – 2011-05-04 01:49:51

+1

最近,我不得不手動編寫SQL查詢,因爲正確寫入需要200ms才能執行,而在使用更新時,我必須等待30分鐘。這就是爲什麼。 – Xowap 2015-02-08 02:02:08

4

UPDATE聲明不支持GROUP BY。見例如PostgreSQL DocsSQLite Docs

您需要成才這樣的:

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

相當於DjangoORM:

from django.db.models.expressions import RawSQL 

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

或:

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

您是否能夠運行最後的建議?不.filter(relation = F('relation__pk'))'等同於WHERE(relation.id = relation.id)',因此不加入任何東西? – jnns 2016-08-12 22:27:01

+0

感謝您的更新。 '.extra()'子句是金子! – jnns 2016-08-13 21:36:42

-1

您可以定義自己的自定義對象經理:

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

然後在你的代碼:

q = Realation.rating_objects.annotated() 

添加ARGS/kwargs定製什麼這位經理返回。

0

解決方法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)