Django ModelForm: Para que save (commit = False) é usado?

88

Por que eu usaria em save(commit=False)vez de apenas criar um objeto de formulário da ModelFormsubclasse e executar is_valid()para validar o formulário e o modelo?

Em outras palavras, para que serve save(commit=False)?

Se você não se importa, vocês poderiam fornecer situações hipotéticas em que isso possa ser útil?

sgarza62
fonte

Respostas:

110

Isso é útil quando você obtém a maioria dos dados do modelo de um formulário, mas precisa preencher alguns null=Falsecampos com dados que não são do formulário.

Salvar com commit = False obtém um objeto de modelo, então você pode adicionar seus dados extras e salvá-los.

Este é um bom exemplo dessa situação.

dokkaebi
fonte
Mas então, se isso fornece um objeto de modelo, como isso difere de atribuir um objeto instanciado anteriormente e atribuí-lo ao ModelForm? (ie form = forms.SampleForm(instance = models.Sample))
OzzyTheGiant
Você precisa commit=Falsese estiver processando seu formulário em um CBVcom def form_valid? Você pode apenas usar form.instance.[field]para atualizar?
alias51
Vamos para 100 :)
dani herrera
39

Aqui está a resposta ( dos documentos ):

# 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)

A situação mais comum é obter a instância do formulário, mas apenas 'na memória', não no banco de dados. Antes de salvá-lo, você deseja fazer algumas alterações:

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

# Save the new instance.
>>> new_author.save()
dani herrera
fonte
1
Você precisa commit=Falsese estiver processando seu formulário em um CBVcom def form_valid? Você pode apenas usar form.instance.[field]para atualizar?
alias51
14

Dos documentos do Django:

Este método save () aceita um argumento opcional de palavra-chave commit, que aceita True ou False. Se você chamar save () com commit = False, ele retornará um objeto que ainda não foi salvo no banco de dados.

Nesse caso, cabe a você chamar save () na instância de modelo resultante. Isso é útil se você quiser fazer um processamento personalizado no objeto antes de salvá-lo ou se quiser usar uma das opções de salvamento de modelo especializado. commit é True por padrão.

Parece que save (commit = False) cria uma instância de modelo, que retorna para você. O que é ótimo para algum pós-processamento antes de realmente salvá-lo!

AJRouvoet
fonte
10

Como um "exemplo real", considere um modelo de usuário onde o endereço de e-mail e o nome de usuário são sempre os mesmos, e então você pode sobrescrever o método save do seu ModelForm como:

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

Se você não usou commit=Falsepara definir o nome de usuário para o endereço de e-mail, teria que modificar o método de salvamento do modelo de usuário ou salvar o objeto de usuário duas vezes (o que duplica uma operação cara do banco de dados).

Mark Chackerian
fonte
Você precisa commit=Falsese estiver processando seu formulário em um CBVcom def form_valid? Você pode apenas usar form.instance.[field]para atualizar?
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()

aqui está meu exemplo de uso de save (commit = False). Eu queria verificar que tipo de arquivo um usuário carregou antes de salvá-lo no banco de dados. Também queria obter a data em que foi anexado, pois esse campo não estava no formulário.

Kollyn Lund
fonte
este é um código python, você não pode executá-lo em um snippet de código
Ayoub Benayache