[Fixed]-Multiple user type sign up with django-allauth

19👍

TL;DR

All the messy stuff I wrote above are junk!

The (final) right solution

In settings.py remove ACCOUNT_SIGNUP_FORM_CLASS, we won’t use it.

Suppose to have the following models:

class PrivateUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

class CompanyUser(models.Model):
    contact_person = models.OneToOneField(User, on_delete=models.CASCADE)
    company_name = models.CharField(max_length=50, null=False, blank=False)

Now, what we want is to let our app signup the PrivateUser and the CompanyUser with different forms.

To accomplish that we’ll extends the django-allauth’s SignupForm and SignupView.

In forms.py:

from myapp.models import CompanyUser

class CompanySignupForm(SignupForm):
    # declare here all the extra fields in CompanyUser model WITHOUT
    # the OneToOneField to User
    # (N.B: do NOT try to declare Meta class with model=CompanyUser,
    # it won't work!)
    company_name = forms.CharField(max_length=50, required=True, strip=True)

    # Override the save method to save the extra fields
    # (otherwise the form will save the User instance only)
    def save(self, request):
        # Save the User instance and get a reference to it
        user = super(CompanySignupForm, self).save(request)
        # Create an instance of your model with the extra fields
        # then save it.
        # (N.B: the are already cleaned, but if you want to do some
        # extra cleaning just override the clean method as usual)
        company_user = CompanyUser(
            contact_person=user,
            company_name=self.cleaned_data.get('company_name')
        )
        company_user.save()

        # Remember to return the User instance (not your custom user,
        # the Django one), otherwise you will get an error when the
        # complete_signup method will try to look at it.
        return company_user.contact_person

Now, we have CompanyUser model and CompanySignupForm form. Let’s create a CompanyUserSignupView view in views.py with the following code:

class CompanyUserSignupView(SignupView):
    # The referenced HTML content can be copied from the signup.html
    # in the django-allauth template folder
    template_name = 'account/signup_company.html'
    # the previously created form class
    form_class = CompanySignupForm

    # the view is created just a few lines below
    # N.B: use the same name or it will blow up
    view_name = 'company_signup'

    # I don't use them, but you could override them
    # (N.B: the following values are the default)
    # success_url = None
    # redirect_field_name = 'next'

# Create the view (we will reference to it in the url patterns)
company_signup = CompanyUserRegistrationView.as_view()

Last step, the urls.py:

urlpatterns = [
    # ...
    url(
        r'^accounts/signup/company/$',
        views.company_signup,
        name='signup-company'
    ),
]

Now, just use your browser to go to http://localhost:8000/accounts/signup/company (or the proper url pattern based on your configuration).

You will find the extra fields and you can signup a company user.

Now repeat all the previous steps to create a PrivateSignupForm form, a PrivateUserSignupView view and add the proper url pattern to let users signup as privates.

LAST WARNING

The django-allauth default signup url will still works unless you override
it with one of your url… and you should do that!

11👍

I had the same problem. I needed to use allauth for different user profile types. I extended the allauth SignupView and used it as a In my case I have a MemberProfile and PartnerProfile:

#profile models

class MemberProfile(models.Model):
  user = models.OneToOneField(
    settings.AUTH_USER_MODEL,
    on_delete=models.CASCADE,
  )


class PartnerProfile(models.Model):
  user = models.OneToOneField(
    settings.AUTH_USER_MODEL,
    on_delete=models.CASCADE,
  )

I want a separate signup page for each type of profile. Luckily the allauth SignupView stores the user on it’s instance in the form_value() method. I extend the SignupView as ProfileView which expects a profile_class :

#mixin

from allauth.account.views import SignupView
from allauth.account.forms import SignupForm


class ProfileSignupView(SignupView):

  template_name = 'profiles/register.html'
  success_url = ''  # profile specific success url
  form_class = SignupForm
  profile_class = None  # profile class goes here

  def form_valid(self, form):
    response = super(ProfileSignupView, self).form_valid(form)
    profile = self.profile_class(user=self.user)
    profile.save()

    return response

then my views look like this:

#views

from .mixins import ProfileSignupView
from .models import PartnerProfile, MemberProfile

class MemberSignupView(ProfileSignupView):

   success_url = '/member/profile'
   profile_class = MemberProfile


class PartnerSignupView(ProfileSignupView):

    success_url = '/partner/profile'
    profile_class = PartnerProfile

Leave a comment