2017-02-03 10 views
1

私はvalue_listと面白い経験があり、なぜこのように動作しているのかわかりません。values_list djangoはどのように機能しますか?

TestObjectの値をvalue_1value_2に、値2を値1に更新したいとします。値がTestObjectvalue_1value_2には、値に対する外部キーがあります。

これは私のコードです:私は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) 

value_1を持っていますが、任意のvalue_2を持っていません。 この関数を実行した後、最終結果は何も起こりませんでした。調査した後、私はTestObjectが実行した後value_2に更新されましたが見つかりました:

TestObject.objects.filter(id__in=from_values_ids).update(value=value_2) 

が、それはto_values_idsは私がprint to_values_idを追加した理由で遅延ロードを持っているかもしれ思っ

TestObject.objects.filter(id__in=to_values_ids).update(value=value_1) 

を実行した後に戻りました。

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

両方を印刷してみてください。あなたの 'to_values_ids = []'があなたが何かを更新していないことを意味するならば! –

+0

2つの更新が空になる前のリストto_values_idsは、TestObject.objects.filter(id__in = from_values_ids).update(value = value_2)を実行した後に変更されました。なぜそれが起こったのですか? – amm

+0

'QuerySets'は参照ではなく値として返されるので、起こってはいけません... –

答えて

1

あなたが見ている問題は、Django QuerySet values_listがジェネレータを返すためです。 1.9以降、QuerySet.values_listFlatValuesListIterableのような反復可能なクラスを実装しています.は、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

ありがとうございました!しかし、私が今プリントを呼び出すと、to_values_idsに[]をロードしたと思われますが、最初のアップデートを実行した後、アップデートされました。それで、それは私が理解していない、再び更新された怠惰な負荷だけではありません。 – amm

+2

@ammクエリーセットの印刷は、クエリーセットの最初の20個程度の要素だけを照会します。それはクエリーセット全体ではないかもしれないので、キャッシュされません。クエリーセット全体であっても、最適化より一貫性が選択され、クエリーセットはキャッシュされません。クエリーセットを反復したり、 'list()'、 'len()'や 'bool()'を呼び出すと、クエリーセット全体が評価されキャッシュされます。 – knbk

関連する問題