I want to send and an otp with REST Framework

Question:

File "/home/ali/MAKEEN/makeen-tasks/ali/tasks/user/api/services.py", line 24, in generate_otp
self.otp = str(randint(100000, 999999))
AttributeError: ‘str’ object has no attribute ‘otp’

    self._otp = str(randint(100000, 999999))
AttributeError: 'str' object has no attribute '_otp'
[25/Feb/2023 14:04:01] "POST /login1/ HTTP/1.1" 500 101590

services.py

class OTPGenerator:
    def __init__(self, phone_number):
        self.phone_number = phone_number
        self._otp = None


    def generate_otp(self):
        self._otp = str(randint(100000, 999999))
        redis_conf.set(name=self.phone_number, value=self._otp, ex=30)
        otp_code = redis_conf.get('phone_number')
        return otp_code

    def __str__(self):
        return self.otp

    def send(self):
        send_sms(phone_number=self.phone_number, otp=self.otp)


    def value(self):
        return redis_conf.get(self.phone_number)

    def is_valid(self, otp):
        if redis_conf.get(self.phone_number) == otp:
            redis_conf.delete(self.phone_number)
            return True
        return False

views.py



class User(APIView):
    seri_data_phone = serializers.GenerateOTPSerializer

    def post(self, request, *args, **kwargs):
        # serializer = serializers.GenerateOTPSerializer(data=request.data)
        serializer = self.seri_data_phone(data=request.data)
        if serializer.is_valid():
            phone_number = serializer.validated_data.get('phone_number')
            if phone_number:
                otp = services.OTPGenerator.generate_otp(phone_number)
                return Response({'message': f"your otp is {otp}"}, status=status.HTTP_200_OK)
            else:
                return Response({'error': 'phone number not found'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

serializers.py

class GenerateOTPSerializer(serializers.Serializer):
    phone_number = serializers.CharField(max_length=11, required=True)


    def phone_validation(self, phone_number):

        if phone_number[0] == '0' and phone_number[1] == '9' and len(phone_number) == 11 and 
                str(phone_number).isnumeric() == True:
            return phone_number
        else:
            return ValueError('your phone-number contain 11 digits and starts with ZERO and 9')

where is my mistake?

Asked By: Ali Mohammadnia

||

Answers:

def __str__(self):
    return self.otp

should be

def __str__(self):
    return self._otp
Answered By: Marco

You are not calling the generate_otp method on the OTPGenerator instance. You’ve not initialized your OTPGenerator class correctly.

You are calling like this:

otp = services.OTPGenerator.generate_otp(phone_number)

You have not initialized the OTPGenerator class yet. So, when you directly call the method on class, you are actually passing phone_number in self parameter, which causes the error that AttributeError: 'str' object has no attribute 'otp'

What you need to do is to do something like this:

otp = services.OTPGenerator(phone_number=phone_number).generate_otp()

This means that you’re first creating an object of the class, and calling the method generate_otp on that object. This would ensure, that two parameters are passed to the __init__ method, the first one will be self which is the object of your OTPGenerator class, and the second one is phone_number. Now finally, you can call generate_otp method that will actually generate your OTP.

Please note that you use self._otp and self.otp interchangeably in your code. Please use self._otp to be consistent and avoid any AttributeError(s).

Finally, a tip is to use packages like django-phone-verify; development repo here: https://github.com/CuriousLearner/django-phone-verify. This can help you in verifying phone numbers via OTPs and security codes that can be reused.

NOTE: Full disclosure: I’m the creator & maintainer of Django-Phone-Verify

Answered By: Sanyam Khurana