2017-02-03 35 views
1

我有value_list有趣的经验,我不知道为什么它以这种方式行事。values_list django如何工作?

我想更新TestObject中的任何值,其中value_1value_2,任何value_2值为value_1。其中value_1value_2来自Value并且TestObject具有Value的外键。

这是我的代码:

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[]

我解决这个问题,我创建一个新的列表与IDS和它的工作,但仍然需要了解核心Python中是如何工作的呢?任何好的解释。

+0

尝试打印两者。如果你的'to_values_ids = []'这意味着你没有更新任何东西! –

+0

两次更新之前的列表to_values_ids是空的,但在运行TestObject.objects.filter(id__in = from_values_ids).update(value = value_2)后,它已更改。为什么会发生? – amm

+0

'QuerySets'作为值返回,而不是引用,所以不应该发生...... –

回答

1

您遇到的问题可能是由于一个事实,即Django的查询集values_lists回报发电机。由于1.9,QuerySet.values_list实现像FlatValuesListIterable此前1.9迭代类,QuerySet.values_list返回一个实例的ValuesListQuerySet ......这两个返回一个发电机,所以查询每次访问变量时执行(因此你的行为打电话时看到了打印)。

产生的对象的行为很像一个列表,这是混乱的,但如果你需要证明它不是一个列表,试试这个:

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)] 
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记录。差异将是显而易见的。

+0

非常感谢您的回复!但是当我现在调用print时,它应该用[]加载to_values_ids,但是在运行第一次更新之后它会被更新。所以它不仅仅是延迟加载它已经被再次更新,我不明白。 – amm

+2

@amm打印查询集仅查询查询集的前20个元素。由于这可能不是整个查询集,因此它不会被缓存。即使它是整个查询集,也会选择一致性而不是优化,而且查询集不会被缓存。如果迭代查询集或调用'list()','len()'或'bool()',则整个查询集将被计算和缓存。 – knbk