Django F表达式连接字段

x8diyxa7  于 2023-07-01  发布在  Go
关注(0)|答案(5)|浏览(127)

因此,我尝试通过运行以下命令来更新我的模型:

FooBar.objects.filter(something=True).update(foobar=F('foo__bar'))

但我得到了以下错误:

FieldError: Joined field references are not permitted in this query

如果F表达式不允许这样做...我如何实现此更新?

鉴于this ticket中的信息,我现在明白这是不可能的,并且永远不会在django中实现,**但是有没有办法实现这个更新?**也许周围有一些工作?我不想使用循环,因为有超过1000万个FooBar对象,所以SQL比Python快得多。

imzjd6km

imzjd6km1#

为什么不使用raw sql:基于this,它将类似于

from django.db import connection

raw_query = '''
update app_foobar set app_foobar.foobar = 
(select app_foo.bar from app_foo where app_foo.id = app_foobar.foo_id) 
where app_foobar.something = 1; 
'''

cursor = connection.cursor()
cursor.execute(raw_query)
lp0sw83n

lp0sw83n2#

这是Georgi Yanchev对两个模型的答案的实现:

class Foo(models.Model):
    bar = models.ForeignKey(Bar)

Foo.objects \
    .filter(foo_field_1=True) \
    .update(foo_field_2=Subquery(
        Bar.objects \
            .filter(id=OuterRef('bar_id')) \
            .values('bar_field_1')[:1]))
cwdobuhd

cwdobuhd3#

对于任何想要一种更简单的方法来做到这一点,而不是有大量对象的情况下,下面的片段应该工作得很好:

for fooBar in FooBar.objects.filter(something=True):
    fooBar.foobar = fooBar.foo.bar
    fooBar.save(update_fields=['foobar'])

对于常规用例,这不应该表现出太大的性能差异,特别是作为数据迁移的一部分运行时。如果需要进一步优化,您也可以选择使用select_related

xeufq47z

xeufq47z4#

还有另一种方法使用bulk_update()(Django 2.2中的新功能),并针对大量记录进行了优化:

import time
from itertools import islice

batch_size = 2000
objs = FooBar.objects.filter(something=True).annotate(_foobar=F('foo__bar')).iterator(batch_size)
while True:
    ts = time.time()
    batch = list(islice(objs, batch_size))
    if not batch:
        break
    for i in batch:
        i.foobar = i._foobar
    FooBar.objects.bulk_update(batch, ["foobar"])
    print(f"{time.time() - ts:.03f}s [{len(batch)}]")
xj3cbfub

xj3cbfub5#

Django 1.11增加了对子查询的支持。您应该能够做到:

from django.db.models import Subquery, OuterRef

FooBar.objects.filter(something=True).update(
    foobar=Subquery(FooBar.objects.filter(pk=OuterRef('pk')).values('foo__bar')[:1])
)

相关问题