Как создать пользователя и профиль с одной формой в Django? - PullRequest
1 голос
/ 18 февраля 2020

Я создал Clients модель в models.py, предназначенную для профиля клиента (пользователя).

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()

Эта модель подключена к Django пользователям через OneToOneField, называемый user.

Я создал форму, которая может добавлять данные к модели Clients в 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')

Как я могу с помощью этой единственной формы добавить поле username и password, чтобы через OneToOneField он создавал пользователя, подключенного к этому профилю?

РЕДАКТИРОВАТЬ

Новая версия файлов над. Теперь он создает пользователя, но все остальные поля для Clients передаются пустыми.

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})

1 Ответ

1 голос
/ 18 февраля 2020

Поля, которые вы добавили, являются обычными полями формы, 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 сделает ваш код более читабельным. Вы всегда должны использовать единственное число для своих моделей. Таким образом, вы можете сделать 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
...