Search code examples
djangodjango-rest-frameworkdjango-rest-auth

django-rest-auth: Custom Register Serializer not saving custom fields


NOT A DUPLICATE

I am building a simple Todo app with Django & ReactJs, and i have ran into a problem with django-rest-auth.

the problem is that my custom UserRegisterSerializer only saves a fragment of the request.dataand ignore the rest. and i have no idea why.

Here's my code:

User Model:

class User(AbstractUser):
    id = models.UUIDField(default=uuid.uuid4,
                      primary_key=True, unique=True, editable=False, db_index=True)

    first_name = models.CharField(max_length=100, blank=False)
    last_name = models.CharField(max_length=100, blank=False)

    email = models.EmailField(max_length=247, blank=False, 
                unique=True, error_messages={
                    'unique': _('A user with this email already exists.'),
                    'invalid': _('Invalid e-mail address detected.'),
                    'blank': _('this field may not be blank.')
                })

    phone = models.CharField(max_length=20, blank=True, null=True, 
                unique=True, error_messages={
                     'unique': _('A user with this phone number already exists.'),
                })

    gender = models.CharField(max_length=5, choices=GENDERS, default=NOOP)
    birthday = models.DateField(blank=True, null=True)

    @property
    def user_id(self):
        return self.id

RegisterSerializer(Custom):

class UserRegisterSerializer(RegisterSerializer):
    first_name = serializers.CharField(
        write_only=True, required=True, max_length=100)
    last_name = serializers.CharField(
        write_only=True, required=True, max_length=100)

    birthday = serializers.DateField(required=True, write_only=True)
    phone = serializers.CharField(max_length=20, write_only=True, 
             validators=[
                    UniqueValidator(
                       User.objects.all(),
                       message=_('A user with this phone number already exists.')
                    )
             ])

    gender = serializers.ChoiceField(
        write_only=True,
        choices=[
            ('NO-OP', 'Rather not tell'),
            ('M', 'Male'),
            ('F', 'Female'),
        ],
        default='NO-OP'
    )

    def get_cleaned_data(self):
        cleaned_data = super().get_cleaned_data()
        cleaned_data['first_name'] = self.validated_data.get('first_name', '')
        cleaned_data['last_name'] = self.validated_data.get('last_name', '')
        cleaned_data['phone'] = self.validated_data.get('phone', '')
        cleaned_data['birthday'] = self.validated_data.get('birthday', '')
        cleaned_data['gender'] = self.validated_data.get('gender', '')
        print(cleaned_data)
        return cleaned_data

output of print(cleaned_data):

{'username': 'john_doe', 'password1': 'djangopass', 'email': 'john_doe@email.com', 'first_name': 'John', 'last_name': 'Doe', 'phone': '+555 555 5555', 'birthday': datetime.date(2020, 6, 3), 'gender': 'M'}

Saved user Model:

saved_model_image


Solution

  • I fixed the issue by using a custom UserAdapter.

    here's the code of adapter.py:

    from allauth.account.adapter import DefaultAccountAdapter
    from allauth.account.utils import user_field
    
    class UserAdapter(DefaultAccountAdapter):
        def save_user(self, request, user, form, commit=True):
             data = form.cleaned_data
             birthday = data.get('birthday')
             phone = data.get('phone')
             gender = data.get('gender')
             if birthday:
                 setattr(user, 'birthday', birthday)
             if phone:
                 user_field(user, 'phone', phone)
             if gender:
                 user_field(user, 'gender', gender)
             return super().save_user(request, user, form, commit=commit)
    

    then in your settings.py:

    ...
    ACCOUNT_ADAPTER = 'accounts.adapter.UserAdapter'
    ...