Following solution worked for me. I used formsets to create this solution.
My models were as follows,
Models:
#Custom user model
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
#Related model(One-to-One relationship with custom user)
class Student(models.Model):
user = models.OneToOneField(CustomUser,on_delete = models.CASCADE)
first_name = models.CharField(max_length=50)
middle_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
After that I created two ModelForms
Forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser,Student
from django import forms
# Form for custom user
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('email', 'password1', 'password2')
class StudentCreationForm(forms.ModelForm):
class Meta:
model = Student
fields = ['user','first_name','middle_name','last_name']
Now the main part, I created a simple inline formset factory to handle Student model as an inline form.
Formset
from django.forms import inlineformset_factory
from .models import CustomUser,Student
from .forms import StudentCreationForm
# As parameters I provided parent Model(CustomUser),child Model(Student) and the Child
# ModelForm(StudentCreationForm)
StudentCreationFormSet = inlineformset_factory(CustomUser, Student,form=StudentCreationForm,extra=1,can_delete = False)
In views, I created the SignUpForm and StudentCreationFormSet object respectively. And in the POST request first I validated the CustomUser form and saved it without comitting it(commit=False). I created an object of custom user and passed it as a instance to the StudentCreationFormSet to validate the related form. If everything goes fine my both forms will be saved else the errors will be shown in the template.
View
from django.shortcuts import render,redirect
from .forms import SignUpForm
from .formsets import StudentCreationFormSet
def createAccountView(request):
student_account_form = SignUpForm()
student_details_formset = StudentCreationFormSet()
if request.method == 'POST':
student_account_form = SignUpForm(request.POST)
if student_account_form.is_valid():
# Validating Custom User form and creating object of it(not comitting as formset needed to be verified)
student_account = student_account_form.save(commit=False)
# Getting Custom User object as passing it as instance to formset
student_details_formset = StudentCreationFormSet (request.POST,instance=student_account)
if student_details_formset.is_valid():
student_account_form.save()
student_details_formset.save()
return redirect('login')
else:
student_details_formset = StudentCreationFormSet (request.POST)
context = {
'student_account_form':student_account_form,
'student_details_form':student_details_formset
}
return render(request, 'account/createStudentPage.html',context=context)
Also note that I am passing both the form and formset in single post request.
Template (createStudentPage.html)
<form method="POST" >
{% csrf_token %}
{{ student_account_form.as_p }}
{{ student_details_form.as_p }}
<button type="submit">Sign Up</button>
</form>