Why am I overwriting my inputting user?

Apparently, every time someone adds a new user to my app they seem to be overwriting their information in the User table with the new person’s. Why?

I don’t use a registration page per se; I have a logged in person ‘registering’ others with all this other information that goes in to User, Profile, Location and Human_notes tables.

I have an input page made of several forms that comprise these several models and here is my view method:

    if request.method == 'POST':
        user_form = UserForm(request.POST, instance=request.user)
        #human_form = HumanForm(request.POST, request.FILES, instance=request.user.profile)
        profile_form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
        location_form = LocationForm(request.POST, request.FILES)
        human_notes_form = HumanNotesForm(request.POST)
        if user_form.is_valid() and profile_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
            location_form.save()
            user_form.save()  
            usr_curr = User.objects.last()  #latest('id')
            prf = profile_form.save(commit=False)
            prf.user = usr_curr
            prf.intaker = request.user 
            # this is to ID the signed in person who is registering the new user...
            loc_curr = Location.objects.last()
            loc_curr_id = loc_curr.id
            prf.location = loc_curr
            profile_form.save()
            messages.success(request, f"User Profile data was saved successfully.")
            humnote = human_notes_form.save(commit=False)
            humnote.intaker = request.user
            humnote.user = usr_curr
            human_notes_form.save()
            messages.success(request, f"Human data was saved successfully.")
            return redirect('/intake/humans')
        else:
            context = {
                'user_form': user_form,
                'profile_form': profile_form,
                'location_form': location_form,
                'human_notes_form': human_notes_form,
            }
    else:
        context = {
            'user_form': UserForm,
            'profile_form': ProfileForm,
            'location_form': LocationForm,
            'human_notes_form': HumanNotesForm,
        }
    return render(request, 'intake/human-add.html', context)```

My User model is the default one (I just learned too late I should have done a custom one) and I have a Profile Model that has a OneToOne key to User, and 

```class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE) 
    intaker = models.ForeignKey(User, default=None, on_delete=models.CASCADE, related_name='profiles', null=True, blank=True,) 
    location = models.OneToOneField('Location', on_delete=models.CASCADE, null=True, related_name='+')
    ...
    @receiver(post_save, sender=User)  
    def create_user_profile(sender, instance, created, **kwargs):
         if created:
             Profile.objects.create(user=instance)

    @receiver(post_save, sender=User) 
    def save_user_profile(sender, instance, **kwargs):
         instance.profile.save()

    def __str__(self):
        return "%s %s %s" % (self.id, self.user, self.phn1)```
    
Thanks for any guidance!

Just a note for future reference - when you post code here, please enclose it between lines consisting of only three backtick - ` characters. That means you’ll have one line of ```, then your code, then another line of ```. This preserves the formatting of the code making it a lot easier to read - especially with if statements where indentation is critical.

example:

# The line above this is ```
def function(self):
    return self
# The line after this is ```

If you can, please edit your post to put the ``` before and after the code.

Ken

1 Like

I suspect the problem is around a lack of understanding with the model form’s save function. You may want to check out the documentation.

If my assumptions are correct this block of code is the issue:

Rather than using the instance returned by profile_form.save(commit=False) in this case, try the following:

profile_form.instance.user = usr_curr
profile_form.instance.intaker = request.user
# this is to ID the signed in person who is registering the new user…
loc_curr = Location.objects.last()
loc_curr_id = loc_curr.id
profile_form.instance.location = loc_curr
profile_form.save()

If you want to keep with save(commit=False) here’s what you need:

prf = profile_form.save(commit=False)
prf.user = usr_curr
prf.intaker = request.user
loc_curr = Location.objects.last()
loc_curr_id = loc_curr.id
prf.location = loc_curr
prf.save()
profile_form.save_m2m()

In addition to @CodenameTim response above, there’s also this issue:

You mentioned that you’ve got a person performing the registrations who is not the person being registered.

However, in your UserForm and ProfileForm retrievals above, you’re setting the instance to request.user - which is the person who is actually submitting the form and not the person for whom the request is being submitted. Since you’re setting the variable profile_form to the profile for the person using that page, that’s the profile being updated.

Ken

Hey, Thanks Ken for the posting guidance… and Tim for the tip. That seems to have worked as I see the new listings in the db with no overwrites!

I was thinking it was maybe using last() to get the last id used…

I am admittedly new to Django and had not seen this syntax:

profile_form.instance.user

Why use .instance?

That save_m2m() threw me for a second, the docs are not clear, I guess you are supposed to use that with forms even if there is no many to many relation in the models (none are here)?

Here’s my opinion:

Using .instance will give you access to the model instance that the form is operating on. It’s partially a matter of preference and what you need to do. Your usage isn’t complicated so you have a number of options available to you.

The save_m2m() is only required if you have ManyToManyField references on the form that you would need to save after calling form.save(commit=False). If you don’t have any, it’s not necessary. However robust code should be prepared for the future. Using that as a goal I’d recommend against using .save(commit=False) and saving the instance afterwards. If you were to use that approach, you’d risk adding a new field to the form and missing that the saves aren’t taking place because of the missing follow-up save_m2m() call.

A form is different from a model. A form is an internal representation of what you see in the browser. A model is the internal representation of what’s in your database.

When you’re working with model forms, the instance attribute is a reference to the model associated with that instance of the form. Setting profile_form.user is overwriting the form field - it doesn’t affect the model. Since you want to change the model, you need the model associated with that form - profile_form.instance.

Regarding the save_m2m - from the save() docs:

Calling save_m2m() is only required if you use save(commit=False) . When you use a save() on a form, all data – including many-to-many data – is saved without the need for any additional method calls.

Ken, VERY helpful insight - and apparently hard to find…

Thank you!!

Thanks for the guidance!