I want to create account using phone number as a username and generate otp code . now i want to validate my phone number. I create a method is_phone_valid to validate my phone number but call it by post method but i am getting an error
This is my views.py
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
return self.is_phone_valid(phone_number)
models.py
class TempRegistration(models.Model):
phone_number = models.CharField(max_length=45)
otp_code = models.CharField(max_length=6)
def __str__(self):
return self.phone_number
Seems like you are using Django REST Framework. If so, use field-level--DRF doc validation of DRF Serializers, as below
# views.py
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
# serializers.py
class AccountsSerializer(serializers.ModelSerializer):
def validate_phone_number(self, phone_number):
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return phone_number
else:
raise serializers.ValidationError('No. not matching')
class Meta:
model = TempRegistration
fields = '__all__'
Note: You don't need to override the post(...) method of CreateAPIView class.
The traceback you have posted explains that it can't find the phone_number you are passing to self.is_phone_valid(phone_number) in your post method.
Secondly, your question is how to call the is_phone_valid from inside the class? You need to make pass self as the first arg of this method.
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(self, phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
phone_number = request.data['phone_number']
return self.is_phone_valid(phone_number)
You have to define the phone_number variable. I hope you're using DRF, then the parameter phone_number will be available in request.data
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
phone_number = request.data['phone_number']
return Response({'result' : self.is_phone_valid(phone_number)})
If you want to extract the phone number in the POST parameters [wiki], then you should access this in self.request.POST.
Furthermore you can not return a boolean as the result of a request. This needs to be a HttpResponse, for example a JsonResponse object [Django-doc]:
from django.http import JsonResponse
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(self, phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
return JsonResponse(
{'result': self.is_phone_valid(self.request.POST.get('phone_number'))}
)
Related
This is my code.
class MyViewSet(ModelViewSet):
serializer_class = MySerializer
queryset = MyClass.objects.all()
def get_serializer_class(self):
if request.user.is_superuser:
return self.serializer_class
else:
return OtherSerializer
def perform_create(self, serializer):
if request.user.is_superuser:
if serializer.is_valid():
serializer.save(organization=self.request.user.organization)
else:
employee = Employee.objects.get(user=self.request.user)
serializer.save(employee=employee, organization=self.request.user.organization)
This is my Serializer:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
def validate(self, data):
employee = data.get('employee')
members = Team.objects.get(id=team.id.members.all())
if employee not in members:
raise serializers.ValidationError('Invalid')
return data
The issue is, My custom validate function is not being called when I call it inside perform_create() in my ViewSet.
What might be the issue?
validate member function should be defined in the scope of the serializer class not inside class Meta. So you need to left-indent the function validate:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
def validate(self, data):
employee = data.get('employee')
members = Team.objects.get(id=team.id.members.all())
if employee not in members:
raise serializers.ValidationError('Invalid')
return data
I have been working on DRF. I usually prefer writing business logic on another python file rather than views.py. I tried to get current logged in user in views.py using
self.request.user
But I don't know how can I get same user info in my business logic file here is what I have came upto now in Views.py
class AddBusCompanyStaffView(generics.CreateAPIView):
serializer_class = AddBusCompanyUserSerializer
def get_bus_company(self):
return GetBusCompanyUseCase(bus_company_id=self.kwargs.get('bus_company_id')).execute()
def perform_create(self, serializer):
print(self.request.user)
return AddBusCompanyUserUseCase(serializer=serializer,
bus_company=self.get_bus_company()
).execute()
I want user in my business logic section here -->usecases.py
User = get_user_model()
class AddBusCompanyUserUseCase(BaseUseCase):
"""
use this to add bus company of specific bus company
"""
def __init__(self,
serializer: bus_company_user_serializers.AddBusCompanyUserSerializer,
bus_company: BusCompany):
self.serializer = serializer
self._data = serializer.validated_data
self._bus_company = bus_company
def execute(self):
self._factory()
def _factory(self):
user_position = self._data.pop('position')
user_password = self._data.pop('password')
user = User(**self._data)
user.set_password(user_password)
user.save()
bus_company_user=BusCompanyStaff(user=user,
position=user_position,
staff_of=self._bus_company,
created_by= #logged in user here
)
how can I get self.request.user in created_by?
my baseusecase is
class BaseUseCase:
"""
Base Use Case
"""
def execute(self):
raise NotImplementedError("Subclasses should implement this!")
def _factory(self):
raise NotImplementedError("Subclasses should implement this!")
def is_valid(self):
return True
Since you can not change your BaseUseCase, you have to add the attribute to the AddBusCompanyUserUseCase class.
The key here is to pass self.request.user to your AddBusCompanyUserUseCase when created so you can access later as self.user attribute.
#usecases.py
class AddBusCompanyUserUseCase(BaseUseCase):
"""
use this to add bus company of specific bus company
"""
def __init__(self,
serializer: bus_company_user_serializers.AddBusCompanyUserSerializer,
bus_company: BusCompany,
user: None,
):
self.serializer = serializer
self._data = serializer.validated_data
self._bus_company = bus_company
self.user = user
def execute(self):
self._factory()
def _factory(self):
user_position = self._data.pop('position')
user_password = self._data.pop('password')
user = User(**self._data)
user.set_password(user_password)
user.save()
bus_company_user=BusCompanyStaff(user=user,
position=user_position,
staff_of=self._bus_company,
created_by= self.user
)
# views.py
class AddBusCompanyStaffView(generics.CreateAPIView):
serializer_class = AddBusCompanyUserSerializer
def get_bus_company(self):
return GetBusCompanyUseCase(bus_company_id=self.kwargs.get('bus_company_id')).execute()
def perform_create(self, serializer):
print(self.request.user)
return AddBusCompanyUserUseCase(serializer=serializer,
bus_company=self.get_bus_company(),
user=self.request.user,
).execute()
I have two APIView classes and one ModelSerializer class. The APIView classes are using the serializer class. Is there any way to know which APIView class is calling the serializer class?
I need seperate representation_view for these 2 APIView.
APIView Classes
class OwnerAllListAPIView(APIView):
def get(self, request, *args, **kwargs):
user = self.request.user
all_list = ListName.objects.filter(owner=user).all()
list_serializer = core_slr.ListNameSerializer(all_list, many=True)
return response('Owner list', list_serializer.data, status.HTTP_200_OK)
class ListNameDetailAPIView(APIView):
def get(self, request, *args, **kwargs):
list_name = ListName.objects.filter(id=self.kwargs.get('list_name_id')).first()
list_serializer = core_slr.ListNameSerializer(list_name)
return response('list name detail view', list_serializer.data, status.HTTP_200_OK)
Serializer Class
class ListNameSerializer(serializers.ModelSerializer):
class Meta:
model = ListName
fields = [
'id', 'owner', 'name'
]
def to_representation(self, instance):
ret = super(ListNameSerializer, self).to_representation(instance)
ret['owner'] = f"{instance.owner.first_name} {instance.owner.last_name}"
ret['total_question'] = QuestionBank.objects.filter(list_name=instance).count()
return ret
In the to_representation View, I just wanna to know which API is currently calling the serializer.
You can easily do that by accessing the context of the Serializer, like the following:
...
def to_representation(self, instance):
ret = super(ListNameSerializer, self).to_representation(instance)
ret['owner'] = f"{instance.owner.first_name} {instance.owner.last_name}"
ret['total_question'] = QuestionBank.objects.filter(list_name=instance).count()
if self.context['view'].action == 'retrieve':
pass # Do something
elif self.context['view'].action == 'list':
pass # Do the Other Thing
return ret
I can`t update users because Django gives me this error in postman:
AttributeError at /profesionales/
Got AttributeError when attempting to get a value for field `user` on serializer `ProfesionalesSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'user'.
Request Method: PUT
Request URL: http://127.0.0.1:8000/profesionales/
Django Version: 1.11.6
Python Executable: C:\Users\Ismael\AppData\Local\Programs\Python\Python36\python.exe
Python Version: 3.6.3
Here is my code:
view.py
#Listar todos los profesionales o crear uno
#profesionales/
class ProfesionalesList(APIView):
def get_object(self, pk):
try:
return User.objects.get(username=pk)
except User.DoesNotExist:
raise Http404
def get(self, request ):
usuarios = Profesionales.objects.all()
usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
return Response(usuarioSerializer.data)
def post(self, request):
profesionalSerializer = ProfesionalesSerializer(data=request.data)
if profesionalSerializer.is_valid():
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
else:
return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, *args, **kwargs):
instance = self.get_object(request.data.get('user').get('username'))
profesionalSerializer = ProfesionalesSerializer(instance, data=request.data)
if profesionalSerializer.is_valid():
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
else:
return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializer(serializers.Serializer):
username = serializers.CharField()
first_name = serializers.CharField(allow_blank=True)
last_name = serializers.CharField(allow_blank=True)
email = serializers.CharField(allow_blank=True)
class Meta:
fields = ('username', 'first_name', 'last_name', 'email')
def create(self, validated_data):
user = User.objects.create(**validated_data)
return user
class ProfesionalesSerializer(serializers.Serializer):
user = UserSerializer()
numColegiado = serializers.CharField(allow_blank=False)
class Meta:
fields = ('user', 'numColegiado')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profesional = Profesionales.objects.create(user=user, **validated_data)
return profesional
def update(self, instance, validated_data):
num_colegiado = validated_data.get('numColegiado')
user_data = validated_data.pop('user')
user = User.objects.get(**user_data)
profesionales = user.profesionales
if num_colegiado:
profesionales.numColegiado = num_colegiado
profesionales.save()
return instance
model.py
class Profesionales(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dni = models.CharField(max_length=9, blank=True, default='')
numColegiado = models.CharField(max_length=8, blank=True, default='')
valoracionMedia = models.FloatField(blank=True, default=0)
numVotos = models.IntegerField(blank=True, default=0)
def __str__(self):
return self.numColegiado
Ok, I have it - but to be honest, you should rethink your API design.
The problem is that - there is no problem -I mean from yout code I cannot reproduce the error. It is probably I assumed the User model - if you can paste the user model definition would be great (or if this is a standard django user - also mention that).
So firstly I would change the serializers to model serializers:
serializers.py
class UsernameValidator(object):
def set_context(self, serializer_field):
"""
This hook is called by the serializer instance,
prior to the validation call being made.
"""
# Determine the existing instance, if this is an update operation.
self.instance = getattr(serializer_field.parent, 'instance', None)
if not self.instance:
# try to get user from profesionales:
root_instance = getattr(serializer_field.root, 'instance', None)
self.instance = getattr(root_instance, 'user', None)
def __call__(self, value):
if self.instance and User.objects.filter(username=value).exclude(id=self.instance.id).exists():
raise ValidationError('Username already exists.')
if not self.instance and User.objects.filter(username=value).exists():
raise ValidationError('Username already exists.')
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=128, validators=[UsernameValidator()])
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class ProfesionalesSerializer(serializers.ModelSerializer):
user = UserSerializer()
numColegiado = serializers.CharField(allow_blank=False)
class Meta:
model = Profesionales
fields = ('user', 'numColegiado')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profesional = Profesionales.objects.create(user=user, **validated_data)
return profesional
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = instance.user
userSerializer = UserSerializer(user, data=user_data)
if userSerializer.is_valid(raise_exception=True):
userSerializer.save()
num_colegiado = validated_data.get('numColegiado')
if num_colegiado:
instance.numColegiado = num_colegiado
instance.save()
return instance
As you can note - I've added the UsernameValidator which is pretty important for API to work properly 0- it basically search for existing user instance and check if username exists or not;
I've also changed the update method - now it is using the UserSerializer explicite; Also corrected some bugs - returning validated_data instead of instance and so on.
At the end views.py:
class ProfesionalesList(APIView):
def get_object(self, pk):
try:
return User.objects.get(username=pk)
except User.DoesNotExist:
raise Http404
def get(self, request ):
usuarios = Profesionales.objects.all()
usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
return Response(usuarioSerializer.data)
def post(self, request):
profesionalSerializer = ProfesionalesSerializer(data=request.data)
if profesionalSerializer.is_valid(raise_exception=True):
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
def put(self, request, *args, **kwargs):
user = self.get_object(request.data.get('user').get('username'))
profesionalSerializer = ProfesionalesSerializer(user.profesionales, data=request.data)
if profesionalSerializer.is_valid(raise_exception=True):
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_200_OK)
I've shorten the code - using the raise_exception in is_valid method.
Actually - sorry for not following the stackoverflow rules - and do not provide an answer for your actual problem - but I strongly believe that analyzing the example you can figure it out. If you have any more questions - please ask.
On your class ProfesionalesSerializer, you have defined user as an instance of UserSerializer. Instead, user should be a field. You can't use serializers as "fields".
Edit: Ignore this. Turns out you can. See here: http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
(thanks to #opalczynski)
Edit
I suspect the whole problem with my UpdateApiView is with the url. No matter how I change it, will return 404.
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone'),
it returns
{
"detail": "Not found."
}
[18/Apr/2016 01:39:02] "PATCH /api/verify-phone/phone_id=00980 HTTP/1.1" 404 4941
Why?
views.py
class VerifyPhone(generics.UpdateAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.VerifyPhoneSerializer
allowed_methods = ['PATCH']
lookup_field = 'phone_id'
def get_queryset(self):
phone_id = self.request.query_params.get('phone_id', None)
queryset = User.objects.filter(phone_id=phone_id)
return queryset
def update(self, request, *args, **kwargs):
print('inside update')
print(request.data)
partial = kwargs.pop('partial', False)
instance = self.get_object()
print(instance)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
print(serializer)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
print('done perform update')
return Response(serializer.data)
serializers.py
class VerifyPhoneSerializer(serializers.ModelSerializer):
regex = r'\d+'
verification_code = serializers.RegexField(regex, max_length=7, min_length=7, allow_blank=False)
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
class Meta:
model = User
fields = ('verification_code', 'phone_id')
def validate(self, data):
verification = api.tokens.verify(data['phone_id'], data['verification_code'])
if verification.response.status_code != 200:
raise serializers.ValidationError("Invalid verification code.")
return data
def update(self, instance, validated_data):
instance.phone_number_validated = True
instance.save()
return instance
Second question Is this correct to get phone_id from the views?
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
Looking at your api url def, I think you should call:
/api/verify-phone/00980
instead of
/api/verify-phone/phone_id=00980
I also think something is wrong with the url def itself (the ^ before \d):
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
should be
url(r'verify-phone/(?P<phone_id>\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
or
url(r'verify-phone/(?P<phone_id>\d{5})$', view.VerifyPhone.as_view(), name='verify-phone')