EDIT
Please, do not waste your time reading the question... it is the wrong approach!
Look at my own answer for a step-by-step guide (with explanation) of the right solution
TL;DR
How could I implement sign up for private and company users using django-allauth?
The approach I'm following (is it correct?)
I have the following models
:
class PrivateUser(models.Model):
"""Models a private user account"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
class CompanyUser(models.Model):
"""Models the company's contact person user account"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Company(models.Model):
"""Models the company attributes"""
contact_person = models.OneToOneField(User, related_name='company')
name = models.CharField(max_length=50, null=False, blank=False)
vat_no = models.CharField(
# some config and validators
)
# ... other non-relevant fields
Now, I have to distinguish between the two users PrivateUser
and CompanyUser
during the sign up process with django-allauth having just one sign up form as specified in the official django-allauth documentation:
ACCOUNT_SIGNUP_FORM_CLASS (=None)
A string pointing to a custom form class (e.g.
myapp.forms.SignupForm
) that is used during signup to ask the user for additional input (e.g. newsletter signup, birth date). This class should implement adef signup(self, request, user)
method, where user represents the newly signed up user.
So, to create a unique form I created an abstract model class with all the
fields from the PrivateUser
and the CompanyUser
plus one (note the user_type
field):
class AbstractComprehensiveUser(models.Model):
"""
Little hackish model class needed to handle one single sign up
form for multiple users
"""
USER_TYPE_CHOICES = (
('private', 'Private'),
('company', 'Company'),
)
user_type = models.CharField(
max_length=10,
blank=False,
choices=USER_TYPE_CHOICES
)
# Common fields for either private and company users
first_name = models.CharField(max_length=30, blank=False)
last_name = models.CharField(max_length=30, blank=False)
# Company specific fields
company_name = models.CharField(max_length=50, null=True, blank=True)
company_vat_no = models.CharField(
# some config and validators
null=True,
blank = True
)
# other non-relevant fields
class Meta:
abstract = True
N.B: all the non-common fields have in this class the attributes null=True
and blank=True
.
Then I created my custom SignupForm
as follow:
class SignupForm(forms.ModelForm):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
class Meta:
model = AbstractComprehensiveUser
fields = (
# Field to differentiate from private and company
# user sign up
'user_type',
# Common fields for either private and company users
'first_name', 'last_name',
# Company specifc fields
'company_name', 'company_vat_no', # etc etc
)
The idea, now, is to use a template with two forms:
- the one with hidden
user_type='private'
and just thefirst_name
andlast_name
fields - the one with hidden
user_type='company'
and the fields fromCompany
model
Then, in the SignupForm
I will receive the user_type
field and I could set the proper form, for example:
class PrivateUserSignupForm(forms.ModelForm):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
class Meta:
model = PrivateUser
fields = ('first_name', 'last_name')
The problem is that when I retrieve data in the SignupForm.signup()
method, the User
model is already written in the database.
I would like to do not save it, but just:
- validating it
- receive data in the
signup
method to populate the correct form (PrivateUserSignupForm
orCompanyUserSignupForm
) - validate the form
- in case of no errors save the user and the other models
- in case of error do not save nothing and warn the user about the error(s)
The question are...
- is this approach correct? There's some other way to accomplish this without these compilcation?
- if this approach is correct, how could I handle the workflow described just above?