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?
Answers:
def __str__(self):
return self.otp
should be
def __str__(self):
return self._otp
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
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?
def __str__(self):
return self.otp
should be
def __str__(self):
return self._otp
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