goelv
goelv

Reputation: 2884

How to save data in backend/database for UserProfile in Django

My intention is to create a user profile using Django's User model and a UserProfile model (which basically adds details / fields about the user). I then want to create a registration form that asks to fill fields that are contained in both the User Model and the UserProfile Model (i.e. a single form for fields from both models).

What's happening right now is after entering the necessary data into my form, the view passes and the server does create a User object and even links it to a UserProfile object (as far as I understand, this linkage occurs because of the signal created in the models.py class). However, no information about the UserProfile (in this case, the "location" field) is added and for the life of me, I can't figure out why.

I have the following models.py

class UserProfile(models.Model):
  # This field is required.
  user = models.ForeignKey(User, unique=True, related_name="connector")
  location = models.CharField(max_length=20, blank=True, null=True)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

post_save.connect(create_user_profile, sender=User)

I have the following forms.py (where UserForm is based on the User Model defined by Django)

class UserForm(ModelForm):
    class Meta:
        model = User

class UserProfileForm(ModelForm):
    class Meta:
        model = UserProfile

I have the following views.py

@csrf_protect
def register(request):
    if request.method == 'POST':
        form1 = UserForm(request.POST)
        form2 = UserProfileForm(request.POST)
        if form1.is_valid() and form2.is_valid():
            #create initial entry for user
            username = form1.cleaned_data["username"]
            password = form1.cleaned_data["password"]
            new_user = User.objects.create_user(username, password)
            new_user.save()

            #create entry for UserProfile (extension of new_user object)      
            profile = form2.save(commit = False)
            profile.user = new_user
            profile.save()
            return HttpResponseRedirect("/books/")
    else:
        form1 = UserForm()
        form2 = UserProfileForm()
    c = {
        'form1':form1,
        'form2':form2,
    }
    c.update(csrf(request))
    return render_to_response("registration/register.html", c)

I have the following register.html

<form action="/accounts/register/" method="post">{% csrf_token %}
   <p style="color:red"> {{form.username.errors}}</p>
    {{ form1.as_p }}
    {{ form2.as_p }}
    <input type="submit" value="Create the account">
</form>

Can anyone see what I'm doing wrong? Is there a better way to do this? Thanks in advance!

Upvotes: 0

Views: 2868

Answers (3)

Sergey Lyapustin
Sergey Lyapustin

Reputation: 1936

You problem is because you have multiple forms in one <form></form> you need to use prefix, so you code looks like this:

@csrf_protect
def register(request):
    if request.method == 'POST':
        form1 = UserForm(request.POST, prefix = "user")
        form2 = UserProfileForm(request.POST, prefix = "profile")
        if form1.is_valid() and form2.is_valid():
            #create initial entry for user
            username = form1.cleaned_data["username"]
            password = form1.cleaned_data["password"]
            new_user = User.objects.create_user(username, password)
            new_user.save()

            #create entry for UserProfile (extension of new_user object)      
            profile = form2.save(commit = False)
            profile.user = new_user
            profile.save()
            return HttpResponseRedirect("/books/")
    else:
        form1 = UserForm(prefix = "user")
        form2 = UserProfileForm(prefix = "profile")
    c = {
        'form1':form1,
        'form2':form2,
        }
    c.update(csrf(request))
    return render_to_response("registration/register.html", c)

to exclude user field from UserProfileForm use exclude

class UserProfileForm(ModelForm):
    class Meta:
        model = UserProfile
        exclude = ('user',)

And if you create profile manually, you don't need to use post_save signal

Upvotes: 1

Bartanix
Bartanix

Reputation: 175

In my mind there is no need to use django signals.

if you give me the full project source (if it is possible) i will fix your problem

your used method for embedding two separate form in one form is not recommended and it is not pretty. but if you say you tested passed data and they are correct please first try the following code.

i changed the way of saving user and userprofile objects. notify me.

Remove Signals From Models:

class UserProfile(models.Model):
      user = models.ForeignKey(User, unique=True, related_name="connector")
      location = models.CharField(max_length=20, blank=True, null=True)

And For Views :

@csrf_protect
def register(request):
      if request.method == 'POST':
          form1 = UserForm(request.POST)
          form2 = UserProfileForm(request.POST)
          if form1.is_valid() and form2.is_valid():
               #create initial entry for user
               username = form1.cleaned_data["username"]
               password = form1.cleaned_data["password"]
               new_user = User()
               new_user.username = username
               new_user.set_password(password)
               new_user.save()

               profile = UserProfile()
               profile.user = new_user
               profile.save()
               return HttpResponseRedirect("/books/")
      else:
          form1 = UserForm()
          form2 = UserProfileForm()
      c = {
               'form1':form1,
               'form2':form2,
              }
      c.update(csrf(request))
      return render_to_response("registration/register.html", c)

Upvotes: 0

Rohan
Rohan

Reputation: 53386

As you are saving the profile in view you do not need the post_save signal. Probably, its getting overwritten.

Upvotes: 0

Related Questions