2010-09-06 38 views
16

Tôi muốn cập nhật tất cả các hàng trong queryset bằng cách sử dụng giá trị chú thích.Truy vấn cập nhật Django với chú thích

Tôi có một mô hình đơn giản:

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) 

Và tôi muốn awoid mã này:

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

Và làm cập nhật trong một SQL-yêu cầu bằng cách sử dụng một cái gì đó như thế này:

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

Không hoạt động:

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

hoặc

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

Cũng không hoạt động:

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

Có thể sử dụng ORM Django cho mục đích này? Không có thông tin về việc sử dụng cập nhật()chú thích() cùng nhau trong tài liệu.

+1

Tôi không chắc chắn điều này thậm chí có thể trong SQL tinh khiết? Khi thực hiện một UPDATE trong SQL, tôi không nghĩ rằng nó có thể tham gia các bảng khác. – user27478

+1

Có, có thể - thông qua các truy vấn phụ, ví dụ. 'UPDATE t1 SET a = (SELECT SUM (c) từ t2 trong đó t2.b = t1.b)'; –

+0

Bạn đang thực sự cố gắng làm gì? Bạn đang cố tổng hợp tất cả các giá trị của cột 'xếp hạng' trên bảng 'SigningRelation' và sau đó lưu nó dưới dạng hàng mới trên bảng 'Quan hệ'? – phourxx

Trả lời

-2

Bạn thực sự không thể thực hiện việc này. Hãy xem the code cho updatefollow it through để đọc tốt.

Thành thật mà nói, có vấn đề gì khi đặt thứ gì đó như thế này trong định nghĩa của Người quản lý? Đặt 3 dòng bạn không muốn đưa vào chế độ xem của bạn vào người quản lý, hãy gọi cho người quản lý đó nếu cần. Ngoài ra, bạn đang thực hiện "ma thuật" ít hơn nhiều và khi nhà phát triển tiếp theo xem mã của bạn, họ sẽ không phải sử dụng một vài WTF .. :)

Ngoài ra, tôi tò mò và có vẻ như bạn có thể sử dụng SQL Join with UPDATE statements nhưng đó là một số hackery SQL cổ điển .. vì vậy, nếu bạn đang nghiêng như vậy, bạn có thể sử dụng chức năng Djangos liệu SQL cho rằng;)

+4

"Thành thật mà nói, có gì sai khi đặt một cái gì đó như thế này trong một định nghĩa Manager?" Đó có thể là * nhiều * yêu cầu được gửi tới DB. Ông đặc biệt yêu cầu "một yêu cầu SQL". Ngoài ra, tôi nghĩ các ví dụ mà anh ấy đã thử rất dễ đọc và không giống như ma thuật. –

+1

Gần đây tôi đã phải viết một truy vấn SQL bằng tay vì viết đúng cách mất 200ms để thực thi, trong khi khi sử dụng một bản cập nhật, tôi phải chờ 30 phút. Đó là lý do tại sao. – Xowap

4

UPDATE tuyên bố không hỗ trợ GROUP BY. Xem ví dụ PostgreSQL Docs, SQLite Docs.

Bạn cần someting như thế này:

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

tương đương trong DjangoORM:

from django.db.models.expressions import RawSQL 

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

hay:

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

Bạn có thể chạy đề xuất cuối cùng của mình không? Không '.filter (quan hệ = F ('relation__pk'))' tương đương với 'WHERE (relation.id = relation.id)' và do đó không tham gia bất cứ điều gì? – jnns

+0

Cảm ơn bạn đã cập nhật. Mệnh đề '.extra()' đó là vàng! – jnns

-2

Nếu bạn muốn tránh nhiều cuộc gọi đến các cơ sở dữ liệu, bạn nên sử dụng transaction.atomic.

Tìm hiểu thêm về Django tài liệu: https://docs.djangoproject.com/en/1.9/topics/db/transactions/#controlling-transactions-explicitly

+0

'transaction.atomic' không giảm số lượng cuộc gọi, nó chỉ thực thi rằng toàn bộ giao dịch hoặc thành công hoàn toàn hoặc không thành công mà không sửa đổi bất kỳ điều gì. –

-1

Bạn có thể xác định đối tượng tùy chỉnh riêng quản lý của bạn:

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

Sau đó, trong mã của bạn:

q = Realation.rating_objects.annotated() 

Thêm args/kwargs tùy chỉnh những gì người quản lý này trả về.

0

Giải pháp cho 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) 
Các vấn đề liên quan