Reputation: 2884
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
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
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
Reputation: 53386
As you are saving the profile in view you do not need the post_save
signal. Probably, its getting overwritten.
Upvotes: 0