Django: "Cannot resolve keyword 'email' into field. Choices are: created_at, expires_at, id, otp, user, user_id"

I'm trying to validate an OTP that was sent to the user when registering i have a separate model for otp which has an OneToOne relation with the user

Otp Model

class Verify(models.Model):
user = models.ForeignKey(
    User, on_delete=models.CASCADE, related_name="userverify", blank=False, null=True)
otp = IntegerRangeField(
    min_value=111111, max_value=999999, blank=True, null=True)
created_at = models.DateTimeField(
    _("created at"), auto_now=False, auto_now_add=True, blank=False)
expires_at = models.TimeField(null=True)

the VerifySerializer: I don't specify the model because i only need it to check the validity of the input, although i think this might be the problem

class VerifySerializerBase(serializers.Serializer):
   email = serializers.EmailField()
   otp = serializers.CharField()

the Verify_Email view takes an email and otp value to validate the email

How I'm trying to verify the otp

class Verify_Email(APIView):
    def post(self, request):
        try:
            data = request.data
            serializer = VerifySerializerBase(data=data)
            if serializer.is_valid():
                email = serializer.data['email']
                otp = serializer.data['otp']
                verify = Verify.objects.filter(email=email, otp=otp)
                user = User.objects.filter(email=email)

                if not user[0].exists():
                    return Response({
                        'message': "A user with this email was not found"
                    }, status=status.HTTP_400_BAD_REQUEST)
                elif user[0].is_active:
                    return Response({
                        'message': "This email has already been verified"
                    }, status=status.HTTP_400_BAD_REQUEST)

                elif verify[0].exists():
                    if verify[0].expires_at >= datetime.now():
                        return Response({
                            'message': "This OTP has expired, please request another one"
                        }, status=status.HTTP_400_BAD_REQUEST)
                    elif verify[0].otp != otp:
                        return Response({
                            'message': "This OTP is invalid"
                        }, status=status.HTTP_400_BAD_REQUEST)
                    else:
                        verify[0].delete()
                        user[0].is_active = True
                        user[0].save()
                        return Response({
                            'message': "Email has been verified"
                        }, status=status.HTTP_200_OK)
                return Response({
                    'message': "Something is wrong"
                }, status=status.HTTP_400_BAD_REQUEST)

            return Response({
                'message': "Something is wrong"
            }, status=status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            return Response(str(e), status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None)

and when testing i get

"Cannot resolve keyword 'email' into field. Choices are: created_at, expires_at, id, otp, user, user_id"

from what I understand i think it's telling me that I'm checking for fields that are not in the OTP model fields but I don't think I'm doing that??

The error relates to the models, not the serializer. This line implies that a Verify model has a field email, because you're building a query on that field:

verify = Verify.objects.filter(email=email, otp=otp)

Perhaps you meant to query by user instead, which involves swapping with the following line like so:

user = User.objects.get(email=email)
verify = Verify.objects.filter(user=user, otp=otp)

i think Daniel answer is correct but a little bit bugy instead use

 user = User.objects.get(email=email)

can use

 user = User.objects.filter(email=email).first()
 if user is None:
   raise Exception("")

this is better because you can use your custom error handler another problem is in your model if you have duplicate or more otp code in this situation your queryset like

verify = Verify.objects.filter(user=user, otp=otp)

returned a list of verify objects,obviously it's not good solution. change model like below

 class Verify(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, 
        related_name="userverify", 
        blank=False, 
        null=True
    )
    otp = IntegerRangeField(
        min_value=111111, max_value=999999, blank=True, null=True
    )
    created_at = models.DateTimeField(
        _("created at"), auto_now=False, auto_now_add=True, blank=False
    )
    expires_at = models.TimeField(null=True)
    is_active = models.BooleanField(default=True)

now use

 verify = Verify.objects.filter(user=user, otp=otp, is_active=True)

‍‍‍‍‍‍

Back to Top