Search code examples
django-rest-frameworkdjango-allauth

DRF login api returns E-mail already exists


This is my model (CustomUser):

class CustomUser(AbstractUser):
    status = models.CharField(_("Record Status"), max_length=1, default='A')
    status_dt = models.DateTimeField(_("Status Date"), default=timezone.now)
    email = models.CharField(_("E-mail"), max_length=254, unique=True)
    record_type = models.CharField(_("Record Type"), max_length=1, default='U')
    lang = models.CharField(_("Language"), max_length=2)
    name = models.CharField(_("First and Last Name"), max_length=100, blank=True)
    photo_main = models.ImageField(_("Profile Picture"), upload_to='photos/%Y/%m/%d/', blank=True)
    gender = models.CharField(_("Gender"), max_length=1, default='M')
    dtob = models.DateField(_("Birth Date"), blank=True, null=True)

def __str__(self):
        return self.email

I have the following serializer:

class UserLoginSerializer(serializers.ModelSerializer):

  token = serializers.CharField(allow_blank=True, read_only=True)


  class Meta:
    model = CustomUser
    fields = ('email', 'password', 'token',)
    extra_kwargs = {
                  'password': {'write_only': True} 
    }

  def validate(self, data):
    print('$$$$$$$$$$$$$$$$$$$$$$$$$ validate $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$')
    print('data:')
    print(data)
    user_obj = None  

    ###  E-mail validation 
    email = data.get("email", None)
    if not email:
      raise ValidationError(_('email must be filled out.'))
    try:
          user_obj = get_object_or_404(CustomUser, pk=email)
    except CustomUser.DoesNotExist:
           raise serializers.ValidationError("E-mail does not exist")
    if user_obj and user_obj.status != 'A':
            raise serializers.ValidationError("E-mail does not exist")
    if not user_obj.exists():
      raise ValidationError(_('User does not exist.'))

    ### Password validation
    password = data.get("password", None)
    if not password:
      raise ValidationError(_('Password must be filled out.'))
    if user_obj:
      if not user_obj.check_password(password):
        raise ValidationError(_('Incorrect credentials. Please try again.'))
      elif user_obj.is_active:
        data['user'] = user_obj
    return data
#################################################################################

This is the view:

class UserLoginAPIView(APIView):

    def post(self, request, *args, **kwargs):
        data = request.data
        serializer = UserLoginSerializer(data=request.data)

        if serializer.is_valid():
            user = serializer.validated_data['user']
            django_login(request, user)
            token, created = Token.objects.get_or_create(user=user)
            return Response({"token": token.key}, status=status.HTTP_200_OK)
        else:
            print('%%%%%%%%%%%%%%%%%%%%%%%%%%% serializer errors %%%%%%%%%%%%%%%%%%%%%%%%')
            print(serializer.errors)    
        return Response(data, status=status.HTTP_400_BAD_REQUEST)  

When I execute a POST to my api I get the following error:

%%%%%%%%%%%%%%%%%%%%%%%%%%% serializer errors %%%%%%%%%%%%%%%%%%%%%%%% {'email': [ErrorDetail(string='user with this E-mail already exists.', code='unique')]} Bad Request: /users/api/login/

It seems that Allauth has intercepted the request and validated it as a register request instead of login.

I have tried using rest-auth library and I got the same results.

Any suggestions as to how can I override Allauth I guess?

Thanks!

This is my users/urls.py file:

from django.urls import path, include
from . import views

urlpatterns = [

    path("api/register/", registration_view, name="register"),
    path("api/login/", views.UserLoginAPIView.as_view(), name="login"),
    path("rest-auth/", include('rest_auth.urls')),
]

I am using Token from rest_framework authtoken:

from django.contrib.auth import get_user_model, login as django_login, logout as django_lougout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse, reverse_lazy
from django.views.generic.edit import CreateView
from django.views.generic import DetailView, RedirectView, UpdateView
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
import allauth

Solution

  • Oh! I understood the problem. You are using ModelSerializer which serializes the data according to the model. It is because of that, the serializer is checking if the email is unique (as you defined in your model).

    There are several improvements we could do to your code but in order to make it works and reach the validate function you should change your serializer.

    from rest_framework import serializers
    
    class UserLoginSerializer(serializers.Serializer):
      email = serializers.EmailField(required=False, allow_blank=True)
      password = serializers.CharField(style={'input_type': 'password'})
      token = serializers.CharField(allow_blank=True, read_only=True)
    

    Once inside validate function you will probably get no users as you are looking for pk=email but you didn't define email as pk in your models. Try this instead:

      get_object_or_404(CustomUser, email=email)