Skip to content
Advertisement

Django Extending User Model – Inherit Profile Form from Model

I am following a tutorial to do this in Django 3.1.7.

The problem I’m having here is I’m being forced to repeat my Profile Model in my Profile Form definition.

I want to use forms.ModelForm in my forms.py to inherit my Profile Model and auto-generate the forms. It seems redundant to have to spell everything out again in forms.py when it is already defined in my Models. But I’m not sure how to do that with this architecture.

I’ve tried this approach: https://stackoverflow.com/a/2213802/4144483 But the problem with this is that UserForm is incomplete – ‘password1’ and ‘password2’ don’t exist for model User. This is not a good solution for user registration. I seem to be bound to using UserCreationForm somehow.

#models.py 
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

@receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    instance.profile.save()


#forms.py
rom django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class SignUpForm(UserCreationForm):
    birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')

    class Meta:
        model = User
        fields = ('username', 'birth_date', 'password1', 'password2', )



#views.py
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect
from mysite.core.forms import SignUpForm

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.profile.birth_date = form.cleaned_data.get('birth_date')
            user.save()
            raw_password = form.cleaned_data.get('password1')
            user = authenticate(username=user.username, password=raw_password)
            login(request, user)
            return redirect('home')
    else:
        form = SignUpForm()
    return render(request, 'signup.html', {'form': form})

Advertisement

Answer

I generally use ModelForm instead of CreateUserForm for UserRegistration like this and add password1 and password2 fields in it. also, I check if they both are the same.:

forms.py

class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Repeat Password', widget=forms.PasswordInput)
    email = forms.EmailField(label='Email')
    date_of_birth = forms.DateField(widget=forms.widgets.DateInput(attrs={'type': 'date'}))
    class Meta:
        model = User
        fields = ['username', 'first_name', 'last_name', 'email',
                 ] #these ordering will be as follow in html form

    def clean_password2(self):
        cd = self.cleaned_data
        if cd['password'] != cd['password2']:
            raise forms.ValidationError("Passwords don't match")
        return cd['password2']

Then in views, I create a user and their profile and save the password in encrypted form, and link their profile.

views.py

def register(request):
    u_form = UserRegistrationForm(data=request.POST or None)
    p_form = ProfileForm(data=request.POST or None, files=request.FILES or None)
    if u_form.is_valid() and p_form.is_valid():
        new_user = u_form.save(commit=False)
        new_user.set_password(u_form.cleaned_data['password']) #this saves password in encrypted form instead of raw password
        new_user.save()
        profile = p_form.save(commit=False)
        profile.user = new_user
        profile.save()
        return render(request, 'accounts/register_done.html', {'new_user': user})
    return render(request, 'accounts/register.html', {'user_form': u_form, 'profile_form':p_form})

You can modify it as you like.

Advertisement