# Understanding how saving forms works

**URL:** <https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412>\
**Category:** Forms & APIs\
**Created:** [January 26, 2024, 12:51pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412 "2024-01-26T12:51:21Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![egor\_r](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/egor_r/32/6627_2.png) [@egor\_r](https://forum.djangoproject.com/u/egor_r)\
**Post date:** [January 26, 2024, 12:51pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412/1 "2024-01-26T12:51:21Z")

</div>

Let’s begin with what I was trying to achieve.  
I’ve got a model containing a boolean field and a foreign key:

```auto
class MyModel(models.Model):
    name = models.CharField(max_length=200)

class MyMark(models.Model):
    chkb = models.BooleanField(default=True)
    my_model = models.ForeignKey(MyModel, null=True, blank=True, on_delete=models.CASCADE)

```

I’m displaying a `CreateView/UpdateView` for `MyMark` model. What I wanted to do is to set `my_model` value to `None` if the `chkb` value is `False`, even if `my_model` value is selected by the user in the form.  
Originally I tried to do this as follows:

```auto
class MyMarkCreate(CreateView):
    model = MyMark
    fields = " __all__"

    ...

    def form_valid(self, form):
        if not form.cleaned_data["chkb"]:
            form.cleaned_data.pop("my_model")
        return super().form_valid(form)

```

but that didn’t work as I expected it to - created/updated `MyMark` object still had non-empty `my_model` value. I expected the object to be created from `form.cleaned_data` in `super().form_valid(form)`, but apparently that’s not what’s happening.

I poked around Django code a bit and found that the model instance is created in `django.forms.models.construct_instance()`, which is called from `BaseModelForm._post_clean()`, building instance from `cleaned_data` - so _before_ I alter `cleaned_data` in my `form_valid`.

[This StackOverflow](https://stackoverflow.com/a/20177911/118520) answer suggests calling `form.save(commit=False)` manually and setting field’s value as needed:

```auto
class MyMarkUpdate(UpdateView):
    ...
    def form_valid(self, form):
        if not form.cleaned_data["chkb"]:
            my_mark = form.save(commit=False)
            my_mark.my_model = None
            my_mark.save()

        print(form.cleaned_data)
        return super().form_valid(form)

```

That works, but then I don’t understand _why_ my manually set value is not automatically overwritten again in `super().form_valid(form)`. It should call `form.save()` again after all - or is it not what’s happening? From the `print` I see that `form.cleaned_data` still contains a value for `my_model` field.

So, if anyone could help explain what’s happening here, I’d be grateful.

And another question - is there a better way to achieve my original goal? Maybe writing a custom `clean()` method, dropping `my_model` from `cleaned_data` would be better?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 26, 2024, 1:41pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412/2 "2024-01-26T13:41:44Z")

</div>

> [@egor\_r](#):
>
> I don’t understand _why_ my manually set value is not automatically overwritten again in `super().form_valid(form)`.

Because `form_valid` itself does not create or modify an instance, that work is done earlier in the process.

Your variable `my_mark` is a reference to the `form.instance` object created by the form. It is resaving the object - but it’s what you’ve already modified. Nothing is happening that would change a value in that instance.

> [@egor\_r](#):
>
> is there a better way to achieve my original goal?

Are there other ways? Sure. Are they any better? 🤷‍♂️

---

<div class="post-metadata">

**Author:** ![egor\_r](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/egor_r/32/6627_2.png) [@egor\_r](https://forum.djangoproject.com/u/egor_r)\
**Post date:** [January 26, 2024, 1:58pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412/3 "2024-01-26T13:58:48Z")

</div>

> [@KenWhitesell](#):
>
> Your variable `my_mark` is a reference to the `form.instance` object created by the form.

This is the point I overlooked. Thanks, that explains it!

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 27, 2024, 12:45pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412/4 "2024-01-27T12:45:54Z")

</div>

Oh, and there is one more thing I meant to mention but forgot.

If you look through what the CBVs are doing, you’ll see that `form_valid` essentially boils down to a `save` and `return HttpResponseRedirect`.

There’s really no need for you to call `super().form_valid()` in a CBV if you’re doing the save. You can return your own `HttpResponseRedirect` and avoid the duplicate `save`.

---

<div class="post-metadata">

**Author:** ![egor\_r](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/egor_r/32/6627_2.png) [@egor\_r](https://forum.djangoproject.com/u/egor_r)\
**Post date:** [January 27, 2024, 3:58pm UTC](https://forum.djangoproject.com/t/understanding-how-saving-forms-works/27412/5 "2024-01-27T15:58:37Z")

</div>

Fair enough, thanks again!
