How to change the default username to email, in DRF obtain token login?

Viewed 25

This is my login path

from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
     ...
    path('login/', obtain_auth_token), 
]

When I use this as a login, it takes username(though it takes email as field data) & password and generate the token for me

{
    "username": "manig@gmail.com",
    "password" : "manig@gmail.com"
}

how I want it

{
    "email": "manig@gmail.com", 
    "password" : "manig@gmail.com"
}

I want to replace username with email!! how should I modify this ?

What I have tried till now is: I have made the custom serializer

class AuthTokenSerializer(serializers.ModelSerializer):
    class Meta:
        model = AuthUser
        fields = ('email', 'password')
        extra_kwargs = {'password': {'write_only': True}}
    
    def validate(self, data):
        user_obj = None
        email = data.get('email')
        password = data.get('password')
        if email and password:
            user_obj = AuthUser.objects.filter(email=email).first()
            if not user_obj:
                raise serializers.ValidationError("This email is not registered")
            if not user_obj.check_password(password):
                raise serializers.ValidationError("Incorrect credentials")
        return data
    
    def create(self, validated_data):
        user = AuthUser.objects.get(email=validated_data['email'])
        refresh = RefreshToken.for_user(user)
        return {
            'user': user,
            'token': str(refresh.access_token)
        }

And here is the view for it

class AuthLoginUser(ObtainAuthToken):
    def post(self, request, *args, **kwargs):
        serializer = AuthTokenSerializer(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'first_name': user.first_name,
            'last_name': user.last_name,
            'email': user.email,
            'role': user.role
            })

And this is how url pattern is

urlpatterns = [
    ...
    path('login/', views.AuthLoginUser.as_view()),
] 

And when I do this it does replaces username field with email, but when I login/pass the data through the endpoint

Error that I get, which I shouldn't

It starts giving me this error, user with this email already exists, instead it should have replied with token, first name, lastname email and role, as per the custom serializer and views class.

Kindly help me to mitigate this issue

1 Answers

You should only use ModelSerializer.create to create new instances; you get that error because the serializer performs validation as if you were creating a new instance. Change your serializer:

class AuthTokenSerializer(serializers.Serializer):
    email = serializers.EmailField(write_only=True)
    password = serializers.CharField(write_only=True, style={'input_type': 'password'})
    
    def validate(self, data):
        email = data.get('email')
        password = data.get('password')

        if email and password:
            try:
               user_obj = AuthUser.objects.get(email=email)
               if not user_obj.check_password(password):
                  raise serializers.ValidationError("Incorrect credentials")
               data['user'] = user_obj
            except AuthUser.DoesNotExist:
               raise serializers.ValidationError("This email is not registered")
        else:
            raise serializers.ValidationError("Missing credentials")
        return data
      
    def save(self):
        user = AuthUser.objects.get(email=self.validated_data['email'])
        refresh = RefreshToken.for_user(user)
        return {
            'user': user,
            'token': str(refresh.access_token)
        }

I adjusted the validation and put the code of create inside the save method, but I think you can remove save since you are not using it in your view.

Related