2017-02-03 2 views
1

Ich habe interessante Erfahrungen mit value_list und ich weiß nicht, warum es auf diese Weise handelt.Wie funktioniert values_list django?

Ich möchte jeden Wert in TestObject hat value_1 zu value_2 und jeden Wert_2 zu Wert_1. Wobei value_1 und value_2 vom Typ Wert und TestObject einen Fremdschlüssel zu Value hat.

Dies ist mein Code:

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) 

ich mit Testobject getestet hat value_1 aber hat keine value_2. Das Endergebnis war nichts passiert nach dem Ausführen dieser Funktion. Nach der Untersuchung fand ich die Testobject zu value_2 aktualisiert wurde nach dem Lauf:

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

aber es kehrte zurück

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

nach dem Laufen dachte ich kann to_values_ids faul Last hat, der Grund, warum ich print to_values_id hinzugefügt ist.

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) 

Aber ich habe das gleiche Ergebnis. Obwohl mein Druck für to_values_ids[] hat.

Die Art, wie ich es repariere Ich habe neue Listen mit den IDs erstellt und es hat funktioniert, aber muss immer noch den Kern Python verstehen, wie es funktioniert? Jede gute Erklärung.

+0

Versuchen Sie beide zu drucken. Wenn deine 'to_values_ids = []' bedeutet, dass du nichts aktualisierst! –

+0

Die Liste to_values_ids vor zwei Aktualisierungen ist leer, aber nach dem Ausführen von TestObject.objects.filter (id__in = from_values_ids) .update (value = value_2) wurde sie geändert. Warum ist das passiert? – amm

+0

'QuerySets' werden als Werte zurückgegeben, nicht als Referenzen, also sollte das nicht passieren ... –

Antwort

1

Das Problem, das Sie sehen, liegt wahrscheinlich an der Tatsache, dass Django QuerySet values_lists Generatoren zurückgibt. Seit 1.9 implementiert QuerySet.values_list eine iterierbare Klasse wie FlatValuesListIterable Vor 1.9 gab QuerySet.values_list eine Instanz von ValuesListQuerySet zurück ... Beide liefern einen Generator, so dass die Abfrage jedes Mal ausgeführt wird, wenn Sie auf die Variable zugreifen (daher das Verhalten, das Sie beim Aufruf sahen drucken).

Das resultierende Objekt wird viel wie eine Liste verhält, was verwirrend ist, aber wenn es eines Beweises bedarf es ist keine Liste, versuchen Sie dies:

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 

... dies wird in Folge:

TypeError: unsupported operand type(s) for +: 'ValuesListQuerySet' and 'list' 

Die Lösung ist einfach Ihre from_values_ids Variable als Liste zu werfen:

from_values_ids = list(TestObject.objects.filter(value=value_1).values_list('id', flat=True)) 

oder erstellen Sie einfach die Liste selbst:

from_values_ids = [obj.id for obj in TestObject.objects.filter(value=value_1)] 
1

Ihr Verständnis ist richtig. Django querysets are lazy. Jetzt

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 

wenn Sie das tun:

TestObject.objects.filter(id__in=from_values_ids).update(value=value_2) 
            |     
            |__> will fetch from db 

Nun sind alle Werte, die value_1 angepasst wurde aktualisiert, um value_2. Nun wird nächste Zeile ausgeführt:

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) 

In diesem Moment alle Objekte, die value_2 abgerufen und aktualisiert value_1

Aber Sie sehen keinen Unterschied überein, da Sie alle value_1 in der Datenbank vor dem Start haben. Daher ruft alle Objekte ab und aktualisiert sie auf value_2 und dann zurück zu value_1. Sehen Sie eine Mischung aus value_1 und value_2 Datensätze in der Datenbank. Der Unterschied wird offensichtlich sein.

+0

Vielen Dank für Ihre Antwort! Aber als ich den Druck jetzt anrief, nahm er an, dass die to_values_ids mit [] geladen wurden, aber nachdem das erste Update ausgeführt wurde, wurde es aktualisiert. Es ist also nicht nur faule Last, es wurde wieder aktualisiert, was ich nicht verstehe. – amm

+2

@amm Beim Drucken eines Abfrage-Sets werden nur die ersten 20 Elemente des Abfrage-Sets abgefragt. Da dies möglicherweise nicht das gesamte Abfrage-Set ist, wird es nicht zwischengespeichert. Auch wenn es sich um das gesamte Abfrage-Set handelt, wird die Konsistenz gegenüber der Optimierung ausgewählt, und das Abfrage-Set wird nicht zwischengespeichert. Wenn Sie das Abfrage-Set iterieren oder 'list()', 'len()' oder 'bool()' aufrufen, wird das gesamte Abfrage-Set ausgewertet und zwischengespeichert. – knbk

Verwandte Themen