ram*_*sus 35 django django-models django-queryset sql-update
我想通过使用带注释的值更新queryset中的所有行.
我有一个简单的模型:
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)
Run Code Online (Sandbox Code Playgroud)
我想要解释这段代码:
for relation in Relation.objects.annotate(total_rating=Sum('sign_relations__rating')):
relation.rating = relation.total_rating or 0
relation.save()
Run Code Online (Sandbox Code Playgroud)
并使用以下内容更新一个SQL请求:
Relation.objects.update(rating=Sum('sign_relations__rating'))
Run Code Online (Sandbox Code Playgroud)
不起作用:
TypeError: int() argument must be a string or a number, not 'Sum'
Run Code Online (Sandbox Code Playgroud)
要么
Relation.objects.annotate(total_rating=Sum('sign_relations__rating')).update(rating=F('total_rating'))
Run Code Online (Sandbox Code Playgroud)
也不起作用:
DatabaseError: missing FROM-clause entry for table "relations_signrelation"
LINE 1: UPDATE "relations_relation" SET "rating" = SUM("relations_si...
Run Code Online (Sandbox Code Playgroud)
为此可以使用Django的ORM吗?没有关于在文档中一起使用update()和annotate()的信息.
Pao*_*rre 55
对于Django 1.11+,您可以使用子查询:
from django.db.models import OuterRef, Subquery, Sum
Relation.objects.update(
rating=Subquery(
Relation.objects.filter(
id=OuterRef('id')
).annotate(
total_rating=Sum('sign_relations__rating')
).values('total_rating')[:1]
)
)
Run Code Online (Sandbox Code Playgroud)
此代码生成与Tomasz Jakub Rup提出的相同的SQL代码,但没有使用RawSQL表达式(Django文档警告您使用它因为SQL注入).
我发表了一篇基于这个答案的文章,有更深入的解释:
在 paulox.net上"使用注释和子查询更新Django查询集"
UPDATE
声明不支持GROUP BY
.参见例如PostgreSQL Docs,SQLite Docs.
你需要这样的东西:
UPDATE relation
SET rating = (SELECT SUM(rating)
FROM sign_relation
WHERE relation_id = relation.id)
Run Code Online (Sandbox Code Playgroud)
相当于DjangoORM:
from django.db.models.expressions import RawSQL
Relation.objects.all(). \
update(rating=RawSQL('SELECT SUM(rating) FROM signrelation WHERE relation_id = relation.id', []))
Run Code Online (Sandbox Code Playgroud)
要么:
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, []))
Run Code Online (Sandbox Code Playgroud)
Bar*_*tek -5
老实说,将这样的东西放在 Manager 定义中有什么问题吗?将您不想放入视图中的这 3 行放入管理器中,并根据需要调用该管理器。此外,你所做的“魔法”要少得多,当下一个开发人员查看你的代码时,他们将不必求助于一些WTF .. :)
另外,我很好奇,看起来你可以将SQL Join 与 UPDATE 语句一起使用,但这是一些经典的 SQL hackery .. 所以如果你愿意,你可以使用 Django 的原始 SQL 功能;)
归档时间: |
|
查看次数: |
6692 次 |
最近记录: |