djangodjango-modelsdjango-rest-frameworkdjango-viewsdjango-rest-viewsets

Django restframework user email already exists error


I am trying to write a login view in django restframe work to authenticate a user into the application but each time I send a request to the server I keep getting this error

`{ 'email': ['email already exist'] }``

here is how I wrote my login serializer

class UserLoginSerializer(serializers.ModelSerializer):
    full_name: str = serializers.CharField(source='get_name', read_only=True)
    tokens: str  = serializers.CharField(source='get_token', read_only=True)
    class Meta:
        model = User
        fields = (
            'id',
            'full_name',
            'email',
            'password',
            'is_superuser',
            'is_seller',
            'created_at',
            'profile_pic',
            'tokens' 
        )
        extra_kwargs = {
            'password' : {'write_only':True, 'required': True},
            'id': {'read_only': True},
            'is_superuser': {'read_only': True},
            'is_seller': {'read_only': True},
            'created_at': {'read_only': True},
            'profile_pic': {'read_only': True}
        }

    def validate(self, data: dict) -> dict:
        email: str = data.get('email', '')
        password: str = data.get('password', '')
        print(password, email) 
        user: auth = auth.authenticate(email=email, password=password)
        print(user)
            
        data = {
            'email': user.email,
            'tokens': user.get_token()
        } 
        return data

and here is how I wrote my loginapiview


class UserLoginAPIView(generics.GenericAPIView):
    serializer_class = UserLoginSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
    


Solution

  • My assumption is it is because you are using a ModelSerializer, so the built in email validator is checking as if it's a create call

    This is my Email User Serializer, shamelessly ripped and edited from rest_framework.authtoken.serializers.AuthTokenSerializer. It's slightly different from yours as it's returning user in validated_data instead of returning it directly from the validate function, so there's a bit extra inside the view.

    from django.contrib.auth import authenticate
    from rest_framework import serializers
    from django.utils.translation import gettext_lazy as _
    
    class EmailUserLoginSerializer(serializers.Serializer):
        """Email login"""
    
        email = serializers.CharField(label=_("email"), write_only=True)
        password = serializers.CharField(
            label=_("Password"),
            style={"input_type": "password"},
            trim_whitespace=False,
            write_only=True,
        )
        token = serializers.CharField(label=_("Token"), read_only=True)
    
        def validate(self, attrs):
            """Validate"""
            email = attrs.get("email")
            password = attrs.get("password")
    
            if email and password:
                user = authenticate(
                    request=self.context.get("request"),
                    email=email,
                    password=password,
                )
    
                # The authenticate call simply returns None for is_active=False
                # users. (Assuming the default ModelBackend authentication
                # backend.)
                if not user:
                    msg = _("Unable to log in with provided credentials.")
                    raise serializers.ValidationError(msg, code="authorization")
            else:
                msg = _('Must include "email" and "password".')
                raise serializers.ValidationError(msg, code="authorization")
    
            attrs["user"] = user
            return attrs
    
    class UserLoginAPIView(generics.GenericAPIView):
        serializer_class = EmailUserLoginSerializer
    
        def post(self, request):
            serializer = self.serializer_class(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data["user"]
            # Note: No need for status=status.HTTP_200_OK as that's the default!
            return Response({
                "email": user.email,
                "tokens": user.get_token(),
            })