Wie verwende ich ein Wörterbuch, um Felder in Django-Modellen zu aktualisieren?

77

Angenommen, ich habe ein Modell wie dieses:

class Book(models.Model):
    num_pages = ...
    author = ...
    date = ...

Kann ich ein Wörterbuch erstellen und dann das Modell damit einfügen oder aktualisieren?

d = {"num_pages":40, author:"Jack", date:"3324"}
TIMEX
quelle
2
Ja. Versuch es. Suchen Sie den **Operator im Python-Referenzhandbuch. docs.python.org/reference/expressions.html#calls
S.Lott
mögliches Duplikat von Update Modell Django durch kwargs
S.Lott

Antworten:

101

Hier ist ein Beispiel für das Erstellen mit Ihrem Wörterbuch d:

Book.objects.create(**d)

Um ein vorhandenes Modell zu aktualisieren, müssen Sie die QuerySet- filterMethode verwenden. Angenommen, Sie kennen das pkBuch, das Sie aktualisieren möchten:

Book.objects.filter(pk=pk).update(**d)
Thierry Lam
quelle
19
Ich denke, Update funktioniert nur auf einem QuerySet, nicht auf einem einzelnen Objekt.
Thierry Lam
13
Seien Sie vorsichtig: update()respektiert keine Signale. Siehe die Antwort von @leech unten.
Wtower
Das ist brilliant.
Joshlsullivan
61

Wenn Sie wissen, dass Sie es erstellen möchten:

Book.objects.create(**d)

Angenommen, Sie müssen nach einer vorhandenen Instanz suchen, können Sie diese mit get oder create finden:

instance, created = Book.objects.get_or_create(slug=slug, defaults=d)
if not created:
    for attr, value in d.items(): 
        setattr(instance, attr, value)
    instance.save()

Wie in einer anderen Antwort erwähnt, können Sie die updateFunktion auch im Queryset-Manager verwenden, aber ich glaube, dass dadurch keine Signale gesendet werden (was für Sie möglicherweise nicht wichtig ist, wenn Sie sie nicht verwenden). Sie sollten es jedoch wahrscheinlich nicht verwenden, um ein einzelnes Objekt zu ändern:

Book.objects.filter(id=id).update()
Blutegel
quelle
Führt Ihr Code nicht zu 2 Datenbanktreffern? Einer zum Erstellen, der andere zum .save()?
Jorge Leitao
2
Ja, aber nicht zu speichern. Wenn es erstellt wurde, gibt es eine, um das Buch nachzuschlagen (SELECT), und eine andere, um es zu aktualisieren (eine UPDATE not INSERT-Anweisung wird generiert). Wenn das Buch nicht existiert, können Sie es nachschlagen (SELECT) und erstellen (INSERT).
Blutegel
7
that will not send any signals out: kann das nicht genug betonen.
Wtower
58

Verwenden Sie diese **Option, um ein neues Modell zu erstellen. Durchlaufen Sie das Wörterbuch und verwenden Sie es setattr(), um ein vorhandenes Modell zu aktualisieren.

Aus Tom Christies Django Rest Framework

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/serializers.py

for attr, value in validated_data.items():
    setattr(instance, attr, value)
instance.save()
Ignacio Vazquez-Abrams
quelle
1
Huh? Kannst du ein Beispiel geben? Also müsste ich eine benutzerdefinierte Funktion schreiben, die das Wörterbuch durchläuft?
TIMEX
1
@ TimEX: Bitte lesen. docs.python.org/reference/expressions.html#calls ist sehr klar, wie dies funktioniert.
S.Lott
1
Können Sie erklären, warum die Verwendung **zum Aktualisieren möglicherweise keine gute Idee ist?
Robin Nemeth
@Pocin using queryset.update(**fields)ist laut django docs eine gute Idee. Zitat: "Wenn Sie nur einen Datensatz aktualisieren und nichts mit dem Modellobjekt tun müssen, ist es am effizientesten, update () aufzurufen, anstatt das Modellobjekt in den Speicher zu laden."
CrowbarKZ
2
Gute Antwort, der Anruf ist sehr explizit. Kein Fan von Filter (...). Update (...), da ein Verpfuschen des Filters zu Massenüberschreibungen führen kann.
Bryant Jackson
1

Wenn Sie bereits ein Django-Objekt haben und dessen Feld aktualisieren möchten, können Sie dies ohne Filter tun. weil Sie es bereits haben, können Sie in diesem Fall:

your_obj.__dict__update(your_dict)
your_obj.save()
Odiljon Djamalov
quelle
0

Zusätzlich zu anderen Antworten ist hier eine etwas sicherere Version, um zu verhindern, dass verwandte Felder durcheinander gebracht werden:

def is_simple_editable_field(field):
    return (
            field.editable
            and not field.primary_key
            and not isinstance(field, (ForeignObjectRel, RelatedField))
    )

def update_from_dict(instance, attrs, commit):
    allowed_field_names = {
        f.name for f in instance._meta.get_fields()
        if is_simple_editable_field(f)
    }

    for attr, val in attrs.items():
        if attr in allowed_field_names:
            setattr(instance, attr, val)

    if commit:
        instance.save()

Es wird überprüft, ob das zu aktualisierende Feld bearbeitet werden kann, kein Primärschlüssel ist und nicht zu den verwandten Feldern gehört.

Anwendungsbeispiel:

book = Book.objects.first()
update_from_dict(book, {"num_pages":40, author:"Jack", date:"3324"})

Die luxuriösen DRF-Serialisierer .createund .update-Methoden haben den begrenzten und validierten Satz von Feldern, was bei manuellen Updates nicht der Fall ist.

Alexey Milogradov
quelle