2010-09-06 12 views
16

Quiero actualizar todas las filas en queryset mediante el uso de un valor anotado.Django queryset de actualización con la anotación

tengo unas modelos simples:

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) 

y quiero awoid este código:

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

Y NO actualización en uno SQL-petición mediante el uso de algo como esto:

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

No funciona:

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

o

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

tampoco funciona:

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

¿Es posible utilizar ORM de Django para este fin? No hay información sobre el uso de update() y annotate() juntos en docs.

+1

No estoy seguro de que esto sea posible, incluso en SQL puro? Al hacer una ACTUALIZACIÓN en SQL, no creo que sea posible unirse a otras tablas. – user27478

+1

Sí, es posible: a través de subconsultas, ej. 'UPDATE t1 SET a = (SELECT SUM (c) desde t2 donde t2.b = t1.b)'; –

+0

¿Qué estás realmente tratando de hacer? ¿Está intentando resumir todos los valores de la columna 'rating' en la tabla 'SigningRelation' y luego guardarlos como una nueva fila en la tabla 'Relation'? – phourxx

Respuesta

-2

Realmente no puedes hacer esto. Eche un vistazo a the code para update y follow it through para una buena lectura.

Honestamente, ¿qué tiene de malo colocar algo así en una definición de Manager? Coloque esas 3 líneas que no quiere poner en su vista en un administrador, llame a ese gerente según sea necesario. Además, estás haciendo mucho menos "magia" y cuando el próximo desarrollador mira tu código, no tendrán que recurrir a algunas WTF ... :)

Además, tenía curiosidad y parece que puede utilizar SQL Join with UPDATE statements pero es cierto hackery SQL clásico .. así que si usted está tan inclinado, puede utilizar la funcionalidad de SQL Djangos prima para eso;)

+4

"Honestamente, ¿qué hay de malo en colocar algo así en una definición de Manager?" Eso podría ser un * lote * de solicitudes enviadas al DB. Específicamente solicitó "una solicitud de SQL". Además, creo que los ejemplos que probó son fáciles de leer y no como magia en absoluto. –

+1

Recientemente tuve que escribir una consulta SQL manualmente porque escrita correctamente me llevó 200ms ejecutarla, mientras que cuando usaba un look de actualización tenía que esperar 30 minutos. Esa es la razón por qué. – Xowap

4

UPDATE declaración no soporta GROUP BY. Ver p. PostgreSQL Docs, SQLite Docs.

Se necesitan calle detrás de esta manera:

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

equivalente en DjangoORM:

from django.db.models.expressions import RawSQL 

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

o:

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

¿Pudo ejecutar su última recomendación? No '.filter (relation = F ('relation__pk'))' equivale a 'WHERE (relation.id = relation.id)' y por lo tanto no une nada? – jnns

+0

Gracias por la actualización. ¡Esa cláusula '.extra()' es oro! – jnns

-1

Puede definir su propio gestor de objetos personalizados:

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

Luego en el código:

q = Realation.rating_objects.annotated() 

Añadir args/kwargs a personalizar lo este gerente regresa

0

Solución para 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) 
Cuestiones relacionadas