iMerica / dj-rest-auth

Authentication for Django Rest Framework
https://dj-rest-auth.readthedocs.io/en/latest/index.html
MIT License
1.68k stars 315 forks source link

Custom Register serializer not detected after switching from django-rest-auth #564

Closed RomainFayolle closed 1 year ago

RomainFayolle commented 1 year ago

Hi !

I'm upgrading a project from Django 3.2.9 to 4.2.5, in a docker container. Previous project requirements.txt :

Django==3.2.9
djangorestframework==3.12.4
django-allauth==0.46.0
django-rest_auth==0.9.5

I upgraded to

Django==4.2.5
djangorestframework==3.14.0
django-allauth==0.58.1
dj-rest-auth==5.0.1

I switched to dj-rest-auth as asked in django-rest-auth https://github.com/Omaraitbenhaddi/django-rest-auth I only changed the imports and the name usage of the packages and run a migrate command.

Expected behavior: Project keep using CustomRegisterSerializer as before Actual behavior: Now, project ignore CustomRegisterSerializer and uses regular RegisterSerializer instead. All resources online point to the settings,py but I have the correct REST_AUTH_REGISTER_SERIALIZERS (I even tried to put my serializer in REST_AUTH_SERIALIZERS instead). I also uninstalled and reinstalled allauth. But keep getting the same issue:

File "/app/cloud_api/apps/user/adapters.py", line 13, in save_user
    user.first_name = data['first_name']
KeyError: 'first_name'

Because wrong Serializer is used. Any idea of what I did wrong in the migration ?

serializers.py

from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from dry_rest_permissions.generics import DRYGlobalPermissionsField
from dj_rest_auth.registration.serializers import RegisterSerializer
from dj_rest_auth.serializers import PasswordResetSerializer

User = get_user_model()

class CustomRegisterSerializer(RegisterSerializer):

    first_name = serializers.CharField(
        max_length=30,
        allow_blank=True,
        allow_null=True,
        required=False
    )

    last_name = serializers.CharField(
        max_length=150,
        allow_blank=True,
        allow_null=True,
        required=False
    )

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

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

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

        return data

adapaters.py

from allauth.account.adapter import DefaultAccountAdapter

from django.contrib.auth import get_user_model

User = get_user_model()

class AccountAdapter(DefaultAccountAdapter):
    def save_user(self, request, user, form, commit=True):
        data = form.cleaned_data
        user.username = data['username']
        user.email = data['email']
        user.first_name = data['first_name']
        user.last_name = data['last_name']

        if 'password1' in data:
            user.set_password(data['password1'])
        else:
            user.set_unusable_password()

        self.populate_username(request, user)
        if commit:
            user.save()

        return user

urls.py

urlpatterns = [
    path(
        'rest-auth/',
        include('dj_rest_auth.urls')
    ),
    path(
        "password-reset/confirm/<uidb64>/<token>/",
        TemplateView.as_view(template_name="password_reset_confirm.html"),
        name='password_reset_confirm'
    ),
    re_path(r'^accounts/', include('allauth.urls'), name='socialaccount_signup'),
    path(
        'rest-auth/registration/',
        include('dj_rest_auth.registration.urls')
    ),
    path('dj-rest-auth/facebook/', FacebookLogin.as_view(), name='fb_login'),
    path('', include(router.urls)),  # includes router generated URL
]

settings.py

INSTALLED_APPS = [
    . . .
    'dj_rest_auth',
    'allauth',
    'allauth.account',
    'dj_rest_auth.registration',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
    . . .
]

MIDDLEWARE = [
    . . .
    'allauth.account.middleware.AccountMiddleware',
]
ACCOUNT_ADAPTER = 'cloud_api'\
                  '.apps.user.adapters.AccountAdapter'
REST_AUTH_SERIALIZERS = {
    'USER_DETAILS_SERIALIZER':
        'cloud_api'
        '.apps.user.serializers.UserSerializer',
    'PASSWORD_RESET_SERIALIZER':
        'cloud_api'
        '.apps.user.serializers.CustomPasswordResetSerializer',
}
REST_AUTH_REGISTER_SERIALIZERS = {
    'REGISTER_SERIALIZER':
        'cloud_api'
        '.apps.user.serializers.CustomRegisterSerializer'
}
RomainFayolle commented 1 year ago

I was able to get another project using the same requirements. In fine, you have to merge your settings into a single var

REST_AUTH = {
    'USER_DETAILS_SERIALIZER':
        'cloud_api.apps.user.serializers.UserSerializer',
    'OLD_PASSWORD_FIELD_ENABLED': True,
    'PASSWORD_RESET_SERIALIZER':
        'cloud_api.apps.user.serializers.CustomPasswordResetSerializer',
    'REGISTER_SERIALIZER':
        'cloud_api.apps.user.serializers.CustomRegisterSerializer',
    'SESSION_LOGIN': False,
}