Reputation: 625
I have created a Clients
model in models.py
that is intended to be a Client (user) Profile.
class Clients(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, verbose_name="Primeiro Nome")
last_name = models.CharField(max_length=30, verbose_name="Apelido")
address = models.CharField(max_length=200, verbose_name="Morada")
nif = models.CharField(max_length=9, verbose_name="NIF", validators=[RegexValidator(r'^\d{1,10}$')], primary_key=True)
mobile = models.CharField(max_length=9, verbose_name="Telemóvel", validators=[RegexValidator(r'^\d{1,10}$')])
email = models.CharField(max_length=200, null=True, verbose_name="Email")
avatar = models.ImageField(null=True)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Meta:
verbose_name_plural = "Clientes"
@receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Clients.objects.create(user=instance)
instance.profile.save()
This model is connected to the Django Users through a OneToOneField called user
.
I created a form that is capable of adding data to the Clients
model in forms.py
:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.core.validators import RegexValidator
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, label="Primeiro Nome")
last_name = forms.CharField(max_length=30, label="Apelido")
address = forms.CharField(max_length=200, label="Morada")
nif = forms.CharField(max_length=9, label="NIF", validators=[RegexValidator(r'^\d{1,10}$')])
mobile = forms.CharField(max_length=9, label="Telemóvel", validators=[RegexValidator(r'^\d{1,10}$')])
email = forms.CharField(max_length=200, label="Email")
class Meta:
model = User
fields = ('username', 'password1', 'password2', 'first_name', 'last_name', 'address', 'nif', 'mobile', 'email')
How can I, through this single form, add a username
and password
field so that, through the OneToOneField, it creates an user connected to this profile?
EDIT
The new version of the files above. Now, it creates the user, but all other fields for the Clients
get passed empty.
My views.py
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.first_name = form.cleaned_data.get('first_name')
user.last_name = form.cleaned_data.get('last_name')
user.address = form.cleaned_data.get('address')
user.nif = form.cleaned_data.get('nif')
user.mobile = form.cleaned_data.get('mobile')
user.email = form.cleaned_data.get('email')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('clientes')
else:
form = SignUpForm()
return render(request, 'backend/new_client.html', {'form': form})
Upvotes: 1
Views: 85
Reputation: 20682
The fields you added are just regular form fields, django doesn't know anything about where to save these values. So you need to override the form's save()
method or save them in your view. Here's how to save them in your view, since this is what you started to do:
if form.is_valid():
user = form.save() # this creates the user with first_name, email and last_name as well!
user.refresh_from_db() # load the profile instance created by the signal
user.clients.address = form.cleaned_data.get('address')
user.clients.nif = form.cleaned_data.get('nif')
user.clients.mobile = form.cleaned_data.get('mobile')
user.clients.save()
login(request, user)
return redirect('clientes')
Note: I don't do anything with first_name
, last_name
and email
in the view, they are fields of the User
model so they will already be saved automatically when you do form.save()
. You should remove them from your Clients
model.
Note 2: Renaming your Clients
model to Client
would make your code more readable. You should always use singular for your models. This way you can do user.client.address
which makes more sense than user.clients.address
since it's a one-to-one field.
Alternatively, you can override the form's save()
method, which is a method I would prefer as I don't think the view should care about how to save the user's profile:
# in SignupForm(UserCreationForm):
def save(self, commit=True):
user = super().save(commit) # this creates the new user
if commit:
user.refresh_from_db() # not sure if this is needed
user.clients.nib = self.cleaned_data.get('nib')
user.clients.address = self.cleaned_data.get('address')
user.clients.mobile = self.cleaned_data.get('mobile')
user.clients.save()
return user
Upvotes: 1