0

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)
    

1
  • what is your authentication backend? Commented Apr 22, 2024 at 7:09

2 Answers 2

1

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(),
        })
Sign up to request clarification or add additional context in comments.

3 Comments

but why do we need to pass the request into authenticate
@kelanidarasimi In this specific situation you don't really have to, as you're using the default django auth, See: django.contrib.auth.backends.ModelBackend authenticate.
@kelanidarasimi But looking at some of the django-rest-framework authentication functions, example: rest_framework.authentication.BasicAuthentication authenticate, they do use the request to rip out the header. sooo, up to you to keep it. imo it's not hurting anybody and the default django auth as it as an argument, so why not?
1

Because you mention a email field in UserLoginSerializer. So email is required in a post request body otherwise serializer through an error.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.