我对value_list
有很多有趣的经历,我不知道它为什么会以这种方式行事。
我想更新TestObject
value_1
到value_2
以及任何value_2到value_1的任何值。来自Value类型value_1
和value_2
的{{1}}和TestObject
的外键值。
这是我的代码:
def _swap(value_1, value_2):
from_values_ids = TestObject.objects.filter(value=value_1).values_list('id', flat=True)
to_values_ids = TestObject.objects.filter(value=value_2).values_list('id', flat=True)
TestObject.objects.filter(id__in=from_values_ids).update(value=value_2)
TestObject.objects.filter(id__in=to_values_ids).update(value=value_1)
我在TestObject上测试过value_1
,但没有任何value_2
。
最终结果是在运行此功能后没有发生任何事情。经过调查,我发现TestObject在运行后已更新为value_2
:
TestObject.objects.filter(id__in=from_values_ids).update(value=value_2)
但是在运行
之后又返回了TestObject.objects.filter(id__in=to_values_ids).update(value=value_1)
我认为可能to_values_ids
有懒惰负载,这就是我添加print to_values_id
的原因。
def _swap(value_1, value_2):
from_values_ids = TestObject.objects.filter(value=value_1).values_list('id', flat=True)
to_values_ids = TestObject.objects.filter(value=value_2).values_list('id', flat=True)
print to_values_ids
TestObject.objects.filter(id__in=from_values_ids).update(value=value_2)
TestObject.objects.filter(id__in=to_values_ids).update(value=value_1)
但我得到了同样的结果。虽然我的to_values_ids
打印件有[]
。
我修复它的方式我创建了带有id的新列表并且它工作但仍需要了解核心python它是如何工作的?任何好的解释。
答案 0 :(得分:1)
您的理解是正确的。 Django querysets are lazy
from_values_ids = TestObject.objects.filter(value=value_1).values_list('id', flat=True) # doesn't hit the db
to_values_ids = TestObject.objects.filter(value=value_2).values_list('id', flat=True) # doesn't hit the db
现在你做的时候:
TestObject.objects.filter(id__in=from_values_ids).update(value=value_2)
|
|__> will fetch from db
现在,所有与value_1
匹配的值都已更新为value_2
。现在执行下一行:
TestObject.objects.filter(id__in=to_values_ids).update(value=value_1)
|
|__> Will actually execute the query you assigned it
# TestObject.objects.filter(value=value_2).values_list('id', flat=True)
此时,所有匹配value_2
的对象都会被提取并更新为value_1
但是你没有看到任何区别,因为你在开始之前在数据库中拥有所有value_1
。因此,from_values_ids
会抓取所有对象并将其更新为value_2
,然后再返回value_1
。请参阅数据库中混合使用value_1
和value_2
条记录。差异很明显。
答案 1 :(得分:1)
您看到的问题可能是由于Django QuerySet values_lists返回生成器。从1.9开始,QuerySet.values_list
实现了一个可迭代的类,如FlatValuesListIterable在1.9之前,QuerySet.values_list
返回了一个ValuesListQuerySet的实例...这两个都返回一个生成器,所以查询是每次访问变量时执行(因此在调用print时看到的行为)。
生成的对象的行为很像列表,这很令人困惑,但如果您需要证明它不是列表,请尝试以下操作:
from_values_ids = TestObject.objects.filter(value=value_1).values_list('id', flat=True)
from_list_ids = [obj.id for obj in TestObject.objects.filter(value=value_1)]
combined_list = from_values_ids + from_list_ids
...这将导致:
TypeError: unsupported operand type(s) for +: 'ValuesListQuerySet' and 'list'
解决方案只是将from_values_ids
变量转换为列表:
from_values_ids = list(TestObject.objects.filter(value=value_1).values_list('id', flat=True))
或者自己生成列表:
from_values_ids = [obj.id for obj in TestObject.objects.filter(value=value_1)]