Django ModelForm: ¿Para qué se usa save(commit=False)?

6 minutos de lectura

avatar de usuario de sgarza62
sgarza62

¿Por qué alguna vez usaría save(commit=False) en lugar de simplemente crear un objeto de formulario desde el ModelForm subclase y en ejecución is_valid() para validar tanto el formulario como el modelo?

En otras palabras, ¿qué es save(commit=False) ¿para?

¿Puede proporcionar situaciones hipotéticas en las que esto podría ser útil?

avatar de usuario de dokkaebi
dokkaebi

Eso es útil cuando obtiene la mayoría de los datos de su modelo de un formulario, pero necesita completar algunos null=False campos con datos que no son de formulario.

Guardar con commit=False obtiene un objeto modelo, luego puede agregar sus datos adicionales y guardarlos.

Este es un buen ejemplo de esa situación.

Aquí esta la documentación sobre el método save. Tenga en cuenta que si su formulario incluye campos de muchos a muchos, también querrá llamar form.save_m2m() después de guardar la instancia del modelo.

  • Pero luego, si esto le da un objeto modelo, ¿en qué se diferencia de asignar un objeto previamente instanciado y asignarlo a ModelForm? (es decir form = forms.SampleForm(instance = models.Sample))

    – Ozzy El Gigante

    18 de julio de 2018 a las 15:13

  • Necesitas commit=False si está procesando su formulario en un CBV con def form_valid? ¿Puedes usar form.instance.[field] ¿actualizar?

    – alias51

    9 de enero de 2020 a las 11:28


  • Vamos al 100 🙂

    – dani herrera

    27 mayo 2020 a las 16:00

  • Sí, pero: ¿puedes explicar entonces por qué se llama Django Antipatterns? django-antipatterns.com/antipattern/…. Estoy viendo este método llamado por todas partes y no puedo encontrar buenas alternativas. ¡gracias!

    – Carlos

    9 de junio de 2022 a las 11:31

  • Gracias por el comentario, @Carlo. Los documentos de Django aún recomiendan el enfoque en esta respuesta (agregué un enlace), pero parece que la solución recomendada por django-antipatterns también funcionaría.

    – dokkaebi

    10 de junio de 2022 a las 18:36


Aquí está la respuesta (de 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)

La situación más común es obtener la instancia del formulario pero solo ‘en la memoria’, no en la base de datos. Antes de guardarlo, desea realizar algunos cambios:

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

# Save the new instance.
>>> new_author.save()

  • Necesitas commit=False si está procesando su formulario en un CBV con def form_valid? ¿Puedes usar form.instance.[field] ¿actualizar?

    – alias51

    9 de enero de 2020 a las 11:29

Avatar de usuario de AJRouvoet
AJRouvoet

De los documentos de Django:

Este método save() acepta un argumento de palabra clave de confirmación opcional, que acepta True o False. Si llamas a save() con commit=Falseluego devolverá un objeto que aún no se ha guardado en la base de datos.

En este caso, depende de usted llamar a save() en la instancia del modelo resultante. Esto es útil si desea realizar un procesamiento personalizado en el objeto antes de guardarlo, o si desea utilizar una de las opciones de guardado de modelos especializadas. commit es True por defecto.

Parece que guardar(commit=False) crea una instancia de modelo, que le devuelve. ¡Lo cual es bueno para un procesamiento posterior antes de guardarlo!

Avatar de usuario de Mark Chackerian
marca chackerian

Como un “ejemplo real”, considere un modelo de usuario donde la dirección de correo electrónico y el nombre de usuario son siempre los mismos, y luego podría sobrescribir el método de guardado de su 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

si no usaste commit=False para establecer el nombre de usuario en la dirección de correo electrónico, debe modificar el método de guardado del modelo de usuario o guardar el objeto de usuario dos veces (lo que duplica una costosa operación de base de datos).

Avatar de usuario de Dev Shah
Dev Shah

Lo básico aquí, entiendo, es que cambia de una instancia de ‘formulario’ a una instancia de ‘modelo’ particular a la vista.

Supongamos que quiero publicar una respuesta como esta en StackOverflow. El código sería así:

# Create a form instance with POST data.
>>> form_data = AnswerForm(request.POST)

# Create, but don't save the new answer instance.
>>> Answer = form_data.save(commit=False)

Así que ahora tenemos que agregar el propietario de esta respuesta y guardarla en nuestra base de datos en la página de visualización de esta manera:

>>> Answer.owner = request.user

>>> Answer.save()

Entonces, de esta manera, podemos agregar el propietario de esta respuesta que no podemos hacer como form_data.owner = request.user en la página de vista y tampoco en la clase de formulario.

Básicamente, cambia de instancia de ‘formulario’ a instancia de ‘modelo’ y luego le permite modificar los datos y guardarlos.

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

aquí está mi ejemplo de usar save(commit=False). Quería verificar qué tipo de archivo cargó un usuario antes de guardarlo en la base de datos. También quería obtener la fecha en que se adjuntó, ya que ese campo no estaba en el formulario.

Avatar de usuario de Shubham Sarda
Shubham Sardá

En palabras simples, aquí actualizamos el objeto de formulario y les informamos que no guarde los valores en la base de datos en este momento, podríamos cambiar alguna entrada con la instancia y luego usar .save() para guardar todos los valores en la base de datos.

Esto nos brinda la flexibilidad de obtener todos los valores del formulario HTML y personalizarlos de acuerdo con nuestros requisitos y luego guardar la instancia.

¿Ha sido útil esta solución?