Django ModelForm: Wofür wird save (commit = False) verwendet?

86

Warum sollte ich jemals verwenden, save(commit=False)anstatt nur ein Formularobjekt aus der ModelFormUnterklasse zu erstellen und auszuführen?is_valid() , um sowohl das Formular als auch das Modell zu validieren?

Mit anderen Worten, wofür ist save(commit=False)?

Wenn es Ihnen nichts ausmacht, könnten Sie dann hypothetische Situationen angeben, in denen dies nützlich sein könnte?

sgarza62
quelle

Antworten:

106

Dies ist nützlich, wenn Sie die meisten Modelldaten aus einem Formular abrufen, aber einige ausfüllen müssen null=False Felder mit Nicht-Formulardaten füllen müssen.

Beim Speichern mit commit = False erhalten Sie ein Modellobjekt. Anschließend können Sie Ihre zusätzlichen Daten hinzufügen und speichern.

Dies ist ein gutes Beispiel für diese Situation.

dokkaebi
quelle
Wenn Sie dann ein Modellobjekt erhalten, wie unterscheidet es sich dann von der Zuweisung eines zuvor instanziierten Objekts und der Zuweisung zur ModelForm? (dh form = forms.SampleForm(instance = models.Sample))
OzzyTheGiant
Benötigen Sie, commit=Falsewenn Sie Ihr Formular in einem CBVmit bearbeiten def form_valid? Können Sie nur form.instance.[field]zum Aktualisieren verwenden?
alias51
Gehen wir zu 100 :)
dani herrera
38

Hier ist die Antwort ( aus den Dokumenten ):

# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)

# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)

Die häufigste Situation besteht darin, die Instanz aus dem Formular abzurufen, jedoch nur im Speicher, nicht in der Datenbank. Bevor Sie es speichern, möchten Sie einige Änderungen vornehmen:

# Modify the author in some way.
>>> new_author.some_field = 'some_value'

# Save the new instance.
>>> new_author.save()
dani herrera
quelle
1
Benötigen Sie, commit=Falsewenn Sie Ihr Formular in einem CBVmit bearbeiten def form_valid? Können Sie nur form.instance.[field]zum Aktualisieren verwenden?
alias51
14

Aus den Django-Dokumenten:

Diese save () -Methode akzeptiert ein optionales Commit-Schlüsselwortargument, das entweder True oder False akzeptiert. Wenn Sie save () mit commit = False aufrufen, wird ein Objekt zurückgegeben, das noch nicht in der Datenbank gespeichert wurde.

In diesem Fall liegt es an Ihnen, save () für die resultierende Modellinstanz aufzurufen. Dies ist nützlich, wenn Sie das Objekt vor dem Speichern benutzerdefiniert verarbeiten möchten oder wenn Sie eine der speziellen Optionen zum Speichern von Modellen verwenden möchten. Commit ist standardmäßig True.

Es scheint, dass save (commit = False) eine Modellinstanz erstellt, die an Sie zurückgegeben wird. Welches ist ordentlich für einige Nachbearbeitung, bevor es tatsächlich gespeichert wird!

AJRouvoet
quelle
10

Stellen Sie sich als "echtes Beispiel" ein Benutzermodell vor, bei dem die E-Mail-Adresse und der Benutzername immer identisch sind. Anschließend können Sie die Speichermethode Ihres ModelForm wie folgt überschreiben:

class UserForm(forms.ModelForm):
    ...
    def save(self):
        # Sets username to email before saving
        user = super(UserForm, self).save(commit=False)
        user.username = user.email
        user.save()
        return user

Wenn Sie commit=Falseden Benutzernamen nicht auf die E-Mail-Adresse festgelegt haben, müssen Sie entweder die Speichermethode des Benutzermodells ändern oder das Benutzerobjekt zweimal speichern (wodurch eine teure Datenbankoperation dupliziert wird).

Mark Chackerian
quelle
Benötigen Sie, commit=Falsewenn Sie Ihr Formular in einem CBVmit bearbeiten def form_valid? Können Sie nur form.instance.[field]zum Aktualisieren verwenden?
alias51
1
            form = AddAttachmentForm(request.POST, request.FILES)
            if form.is_valid():
                attachment = form.save(commit=False)
                attachment.user = student
                attachment.attacher = self.request.user
                attachment.date_attached = timezone.now()
                attachment.competency = competency
                attachment.filename = request.FILES['attachment'].name
                if attachment.filename.lower().endswith(('.png','jpg','jpeg','.ai','.bmp','.gif','.ico','.psd','.svg','.tiff','.tif')):
                    attachment.file_type = "image"
                if attachment.filename.lower().endswith(('.mp4','.mov','.3g2','.avi','.flv','.h264','.m4v','.mpg','.mpeg','.wmv')):
                    attachment.file_type = "video"
                if attachment.filename.lower().endswith(('.aif','.cda','.mid','.midi','.mp3','.mpa','.ogg','.wav','.wma','.wpl')):
                    attachment.file_type = "audio"
                if attachment.filename.lower().endswith(('.csv','.dif','.ods','.xls','.tsv','.dat','.db','.xml','.xlsx','.xlr')):
                    attachment.file_type = "spreasheet"
                if attachment.filename.lower().endswith(('.doc','.pdf','.rtf','.txt')):
                    attachment.file_type = "text"
                attachment.save()

Hier ist mein Beispiel für die Verwendung von save (commit = False). Ich wollte überprüfen, welchen Dateityp ein Benutzer hochgeladen hat, bevor ich ihn in der Datenbank speichere. Ich wollte auch das Datum erhalten, an dem es angehängt war, da dieses Feld nicht im Formular war.

Kollyn Lund
quelle
Dies ist ein Python-Code, den Sie nicht im Code-Snippet
ausführen können