如何在 Django 中使用单一表单创建用户和配置文件?
How create a user and a profile with a single form in Django?
我在 models.py
中创建了一个 Clients
模型,旨在成为客户(用户)配置文件。
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()
此模型通过名为 user
的 OneToOneField 连接到 Django 用户。
我创建了一个能够将数据添加到 forms.py
中的 Clients
模型的表单:
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')
如何通过这个单一表单添加 username
和 password
字段,以便通过 OneToOneField 创建一个连接到此配置文件的用户?
编辑
以上文件的新版本。现在,它创建了用户,但 Clients
的所有其他字段都为空。
我的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})
您添加的字段只是常规表单字段,django 不知道将这些值保存在何处。因此,您需要覆盖表单的 save()
方法或将它们保存在您的视图中。以下是将它们保存在您的视图中的方法,因为这是您开始做的事情:
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')
注意:我没有对视图中的 first_name
、last_name
和 email
做任何事情,它们是 User
模型的字段,所以它们已经当您执行 form.save()
时自动保存。您应该将它们从您的 Clients
模型中删除。
注意 2:将您的 Clients
模型重命名为 Client
会使您的代码更具可读性。您应该始终为您的模型使用 singular。这样你就可以做 user.client.address
这比 user.clients.address
更有意义,因为它是一对一的字段。
或者,您可以覆盖表单的 save()
方法,这是我更喜欢的方法,因为我认为视图不应该关心如何保存用户的个人资料:
# 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
我在 models.py
中创建了一个 Clients
模型,旨在成为客户(用户)配置文件。
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()
此模型通过名为 user
的 OneToOneField 连接到 Django 用户。
我创建了一个能够将数据添加到 forms.py
中的 Clients
模型的表单:
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')
如何通过这个单一表单添加 username
和 password
字段,以便通过 OneToOneField 创建一个连接到此配置文件的用户?
编辑
以上文件的新版本。现在,它创建了用户,但 Clients
的所有其他字段都为空。
我的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})
您添加的字段只是常规表单字段,django 不知道将这些值保存在何处。因此,您需要覆盖表单的 save()
方法或将它们保存在您的视图中。以下是将它们保存在您的视图中的方法,因为这是您开始做的事情:
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')
注意:我没有对视图中的 first_name
、last_name
和 email
做任何事情,它们是 User
模型的字段,所以它们已经当您执行 form.save()
时自动保存。您应该将它们从您的 Clients
模型中删除。
注意 2:将您的 Clients
模型重命名为 Client
会使您的代码更具可读性。您应该始终为您的模型使用 singular。这样你就可以做 user.client.address
这比 user.clients.address
更有意义,因为它是一对一的字段。
或者,您可以覆盖表单的 save()
方法,这是我更喜欢的方法,因为我认为视图不应该关心如何保存用户的个人资料:
# 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