values_list django如何工作?

时间:2017-02-03 18:34:54

标签: python django

我对value_list有很多有趣的经历,我不知道它为什么会以这种方式行事。

我想更新TestObject value_1value_2以及任何value_2到value_1的任何值。来自Value类型value_1value_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它是如何工作的?任何好的解释。

2 个答案:

答案 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_1value_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)]