Search code examples
djangodjango-allauth

How to save in Custom User model a field from allauth's customized SignUp Form?


I have custom user

# custom_user/models.py

class MyUser(AbstractBaseUser, PermissionsMixin):
    USER_ACCOUNT_TYPE_CHOICES = (
        (1, _('organization')),
        (2, _('personal')),
    )

    user_account_type = models.IntegerField(_('account type'), choices=USER_ACCOUNT_TYPE_CHOICES)
    email = models.EmailField(_('email address'), max_length=255, unique=True)

USERNAME_FIELD = 'email'

class MyUserManager(BaseUserManager):
    def _create_user(self, email, password, username=None, **extra_fields):
        """
        Creates and saves a User with the given username, email and password.
        """
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        if username:
            username = self.model.normalize_username(username)
            user = self.model(username=username, email=email, **extra_fields)
        else:
            user = self.model(email=email, **extra_fields)

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, password=None, username=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, username=username, **extra_fields)

    def create_superuser(self, email, password, username=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, username=username, **extra_fields)

In the settings there is info which User Model to use and where to find additional field I need when user signing up.

# settings.py

ACCOUNT_SIGNUP_FORM_CLASS = 'custom_user.forms.SignupForm'
AUTH_USER_MODEL = 'custom_user.MyUser'

I found a lot of topics with different methods to configure allauth's SignupForm, but no one works. One of the method is below:

# custom_user/forms.py

from django.forms import ModelForm
from .models import MyUser

class SignupForm(ModelForm):
    class Meta:
        model = MyUser
        fields = ['user_account_type']

    def signup(self, request, user):
        user.user_account_type = self.cleaned_data['user_account_type']
        user.save()

In a browser I see 2 choices (organization\personal)

Request information POST

Variable    Value
email      '[email protected]'
password2  'Test1234'
csrfmiddlewaretoken     'VvrW6PcR8G8OD3fDzunXbtDh4LdYiwzs5xxaYH4xvj'
password1   'Test1234'
user_account_type   '1'
username    'Test'

so allauth sees the field but can not save the value in DB.

The actual error is:

IntegrityError at /accounts/signup/
null value in column "user_account_type" violates not-null constraint

Is my def signup correct? What else do I need to configure?

Django v1.10, allauth v0.26.1

EDITED: I found the workaround

def signup(self, request, user):
    user_account_type = self.cleaned_data['user_account_type']         
    MyUser.objects.filter(email=user).update(user_account_type=user_account_type)

but in this case user_account_type must also include null=True, blank=True

What should be changed to avoid this workaround and save all information of registration (email, password, account type) in one step? Why allauth does not save user_account_type in the same flow when saves email and password?


Solution

  • Currently, allauth already saves the user before triggering your custom signup method. So, the user gets saved before you get the chance to plug in the user_account_type value.

    You can work around this by using a custom save_user adapter method:

    class MyAccountAdapter(DefaultAccountAdapter):
    
        def save_user(self, request, user, form, commit=True):
            # Insert logic to inspect the signup form, or
            # put some default user values in here...
            user.user_account_type = form.cleaned_data['user_account_type']
            return super(MyAccountAdapter, self).save_user(
                request,
                user,
                form,
                commit)