2010-09-06 77 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)