django-rest-auth自定义注册无法保存额外的字段

时间:2022-09-16 19:19:07

I am using DRF and for login/registration I am using Django-rest-auth.

我正在使用DRF和登录/注册我正在使用Django-rest-auth。

  1. I have customized User model to have extra fields
  2. 我已经定制了用户模型以获得额外的字段

  3. I have custom registration serializer to store extra fields along with username, password while registering a new user.
  4. 我有自定义注册序列化程序,用于在注册新用户时存储额外字段以及用户名,密码。

Registration is successful however, extra fields are not saved along with username, first_name, last_name and password.

注册成功但是,额外的字段不会与username,first_name,last_name和password一起保存。

My model:

class UserManager(BaseUserManager):

  def _create_user(self, username, email, password, is_staff, is_superuser, address, **extra_fields):
    now = timezone.now()
    if not username:
      raise ValueError(_('The given username must be set'))
    email = self.normalize_email(email)
    user = self.model(username=username, email=email,
             is_staff=is_staff, is_active=True,
             is_superuser=is_superuser, last_login=now,
             date_joined=now, address=address, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user

  def create_user(self, username, email=None, password=None, **extra_fields):
    return self._create_user(username, email, password, False, False, True,
                 **extra_fields)

  def create_superuser(self, username, email, password, **extra_fields):
    user=self._create_user(username, email, password, True, True,
                 **extra_fields)
    user.is_active=True
    user.save(using=self._db)
    return user


class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(_('username'), max_length=30, unique=True,
    help_text=_('Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters'),
    validators=[
      validators.RegexValidator(re.compile('^[\w.@+-]+$'), _('Enter a valid username.'), _('invalid'))
    ])
  first_name = models.CharField(_('first name'), max_length=30, blank=True, null=True)
  last_name = models.CharField(_('last name'), max_length=30, blank=True, null=True)
  email = models.EmailField(_('email address'), max_length=255, unique=True)
  is_staff = models.BooleanField(_('staff status'), default=False,
    help_text=_('Designates whether the user can log into this admin site.'))
  is_active = models.BooleanField(_('active'), default=True,
    help_text=_('Designates whether this user should be treated as active. Unselect this instead of deleting accounts.'))
  date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
  receive_newsletter = models.BooleanField(_('receive newsletter'), default=False)
  birth_date = models.DateField(_('birth date'), auto_now=False, null=True)
  address = models.CharField(_('address'), max_length=30, blank=True, null=True)
  phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
  phone_number = models.CharField(_('phone number'), validators=[phone_regex], max_length=30, blank=True, null=True) # validators should be a list

  USER_TYPES = (
    ('Farmer', 'Farmer'),
    ('Windmill owner', 'Windmill owner'),
    ('Solar panel owner', 'Solar panel owner'),)
  user_type = models.CharField(_('user type'), choices=USER_TYPES, max_length=30, blank=True, null=True)

  USERNAME_FIELD = 'username'
  REQUIRED_FIELDS = ['email',]

  objects = UserManager()

  class Meta:
    verbose_name = _('user')
    verbose_name_plural = _('users')

  def get_full_name(self):
    full_name = '%s %s' % (self.first_name, self.last_name)
    return full_name.strip()

  def get_short_name(self):
    return self.first_name

  def email_user(self, subject, message, from_email=None):
    send_mail(subject, message, from_email, [self.email]) 

My Serializer:

class RegisterSerializer(serializers.Serializer):
    email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
    first_name = serializers.CharField(required=True, write_only=True)
    last_name = serializers.CharField(required=True, write_only=True)
    address = serializers.CharField(required=True, write_only=True)

    user_type = serializers.ChoiceField(
    choices=(('Farmer', 'Farmer'),('Windmill owner', 'Windmill owner'),('Solar panel owner', 'Solar panel owner'),),
    style={'base_template': 'radio.html'},
    required=True, write_only=True)


    password1 = serializers.CharField(required=True, write_only=True)
    password2 = serializers.CharField(required=True, write_only=True)

    def validate_email(self, email):
        email = get_adapter().clean_email(email)
        if allauth_settings.UNIQUE_EMAIL:
            if email and email_address_exists(email):
                raise serializers.ValidationError(
                    _("A user is already registered with this e-mail address."))
        return email

    def validate_password1(self, password):
        return get_adapter().clean_password(password)

    def validate(self, data):
        if data['password1'] != data['password2']:
            raise serializers.ValidationError(
                _("The two password fields didn't match."))
        return data

    def get_cleaned_data(self):
        return {
            'first_name': self.validated_data.get('first_name', ''),
            'last_name': self.validated_data.get('last_name', ''),
            'address': self.validated_data.get('address', ''),
            'user_type': self.validated_data.get('user_type', ''),
            'password1': self.validated_data.get('password1', ''),
            'email': self.validated_data.get('email', ''),
        }

    def save(self, request):
        adapter = get_adapter()
        user = adapter.new_user(request)
        self.cleaned_data = self.get_cleaned_data()
        adapter.save_user(request, user, self)
        setup_user_email(request, user, [])
        user.save()
        return user 

What is wrong?

哪里不对?

2 个解决方案

#1


10  

It seems like django-allauth doesn't allow saving custom fields by default:

似乎django-allauth默认情况下不允许保存自定义字段:

(ref: https://github.com/pennersr/django-allauth/blob/master/allauth/account/adapter.py#L227)

To go around it, simply assign the custom field values before doing user.save()

要解决它,只需在执行user.save()之前分配自定义字段值

self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
setup_user_email(request, user, [])

user.address = self.cleaned_data.get('address')
user.user_type = self.cleaned_data.get('user_type')

user.save()
return user

That was a dirty fix. A cleaner way would be to override the allauth adapter to support your custom fields.

这是一个肮脏的修复。更简洁的方法是覆盖allauth适配器以支持您的自定义字段。

#2


0  

To override the default adapter and save the custom fields try the following

要覆盖默认适配器并保存自定义字段,请尝试以下操作

Create an adapters.py file in your app root folder and paste the code below

在app根文件夹中创建adapters.py文件并粘贴下面的代码

from allauth.account.adapter import DefaultAccountAdapter


class CustomUserAccountAdapter(DefaultAccountAdapter):

    def save_user(self, request, user, form, commit=True):
        """
        Saves a new `User` instance using information provided in the
        signup form.
        """
        from allauth.account.utils import user_field

        user = super().save_user(request, user, form, False)
        user_field(user, 'address', request.data.get('address', ''))
        user_field(user, 'first_name', request.data.get('first_name', ''))
        user_field(user, 'last_name', request.data.get('last_name', ''))
        user_field(user, 'user_type', request.data.get('user_type', ''))
        user.save()
        return user

Lastly set the settings configuration to use your custom adapter by adding this line in the settings.py file

最后,通过在settings.py文件中添加此行,将设置配置设置为使用自定义适配器

ACCOUNT_ADAPTER = 'users.adapters.CustomUserAccountAdapter'

#1


10  

It seems like django-allauth doesn't allow saving custom fields by default:

似乎django-allauth默认情况下不允许保存自定义字段:

(ref: https://github.com/pennersr/django-allauth/blob/master/allauth/account/adapter.py#L227)

To go around it, simply assign the custom field values before doing user.save()

要解决它,只需在执行user.save()之前分配自定义字段值

self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
setup_user_email(request, user, [])

user.address = self.cleaned_data.get('address')
user.user_type = self.cleaned_data.get('user_type')

user.save()
return user

That was a dirty fix. A cleaner way would be to override the allauth adapter to support your custom fields.

这是一个肮脏的修复。更简洁的方法是覆盖allauth适配器以支持您的自定义字段。

#2


0  

To override the default adapter and save the custom fields try the following

要覆盖默认适配器并保存自定义字段,请尝试以下操作

Create an adapters.py file in your app root folder and paste the code below

在app根文件夹中创建adapters.py文件并粘贴下面的代码

from allauth.account.adapter import DefaultAccountAdapter


class CustomUserAccountAdapter(DefaultAccountAdapter):

    def save_user(self, request, user, form, commit=True):
        """
        Saves a new `User` instance using information provided in the
        signup form.
        """
        from allauth.account.utils import user_field

        user = super().save_user(request, user, form, False)
        user_field(user, 'address', request.data.get('address', ''))
        user_field(user, 'first_name', request.data.get('first_name', ''))
        user_field(user, 'last_name', request.data.get('last_name', ''))
        user_field(user, 'user_type', request.data.get('user_type', ''))
        user.save()
        return user

Lastly set the settings configuration to use your custom adapter by adding this line in the settings.py file

最后,通过在settings.py文件中添加此行,将设置配置设置为使用自定义适配器

ACCOUNT_ADAPTER = 'users.adapters.CustomUserAccountAdapter'