Django Get Logged In User Info Outsides Views.py - python

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()

Related

Custom Validate function is not being called inside perform_create function in DRF

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

How can i call own method in django class based api views

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'))}
)

Update user error with Django API rest framework

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)

confusion on which serializer to use to update user profile

I have a profile model for user to update. I want to have a rest api for that. I have a profile model which is too big than usual. I am working to update the user profile but I am having confusion on which serializer should i use to update the profile. Here is what i have done till now
I created the serializer for user and user profile.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('token', 'user','current_location', 'permanent_location', 'dob',
'about_me', 'gender_status', 'create_profile_for', 'marital_status',
'height', 'weight', 'body_type', 'complexion',)
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(required=True)
class Meta:
model = User
fields = ('id', 'profile', 'username', 'email', 'first_name', 'last_name',)
def update(self, instance, validated_data):
# instance = super(UserSerializer, self).update(validated_data)
profile_data = validated_data.pop('profile')
#updating user data
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('first_name', instance.email)
#updating profile data
if not instance.profile:
Profile.objects.create(user=instance, **profile_data)
instance.profile.current_location = profile_data.get('current_location', instance.profile.current_location)
instance.profile.permanent_location = profile_data.get('permanent_location', instance.profile.permanent_location)
instance.profile.weight = profile_data.get('weight', instance.profile.weight)
instance.profile.height = profile_data.get('height', instance.profile.height)
instance.profile.about_me = profile_data.get('about_me', instance.profile.about_me)
instance.profile.create_profile_for = profile_data.get('create_profile_for', instance.profile.create_profile_for)
instance.profile.body_type = profile_data.get('body_type', instance.profile.body_type)
instance.save()
return instance
This is my view
class UserProfile(APIView):
serializer_class = UserSerializer
def get(self, request, token=None, format=None):
"""
Returns a list of profile of user
"""
reply={}
try:
profile_instance = Profile.objects.filter(user=self.request.user)
if token:
profile = profile_instance.get(token=token)
reply['data'] = self.serializer_class(profile).data
else:
reply['data'] = self.serializer_class(profile_instance, many=True).data
except:
reply['data']=[]
return Response(reply, status.HTTP_200_OK)
def post(self, request, token=None, format=None):
"""
update a profile
"""
profile=None
if not token is None:
try:
profile = Profile.objects.get(user=request.user, token=token)
except Profile.DoesNotExist:
return error.RequestedResourceNotFound().as_response()
except:
return error.UnknownError().as_response()
serialized_data = self.serializer_class(profile, data=request.data, partial=True)
reply={}
if not serialized_data.is_valid():
return error.ValidationError(serialized_data.errors).as_response()
else:
profile = serialized_data.save(user=request.user)
reply['data']=self.serializer_class(profile, many=False).data
return Response(reply, status.HTTP_200_OK)
How should i handle my user profile update?
Updated my UserSerializer with update function
UPDATE with Dean Christian Armada solution
class UserProfile(APIView):
serializer_class = ProfileSerializer
def get(self, request, token=None, format=None):
"""
Returns a list of profile of user
"""
reply={}
try:
profile_instance = Profile.objects.filter(user=self.request.user)
if token:
profile = profile_instance.get(token=token)
reply['data'] = self.serializer_class(profile).data
else:
reply['data'] = self.serializer_class(profile_instance, many=True).data
except:
reply['data']=[]
return Response(reply, status.HTTP_200_OK)
def put(self, request, token=None, *args, **kwargs):
"""
update a profile
"""
print('token', token)
if token:
try:
profile = Profile.objects.get(token=token)
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
serialized_data = self.serializer_class(profile, data=request.data)
reply={}
if serialized_data.is_valid():
profile = serialized_data.save(user=request.user)
reply['data'] = self.serializer_class(profile, many=False).data
return Response(reply, status.HTTP_200_OK)
else:
return Response(serialized_data.errors, status.HTTP_400_BAD_REQUEST)
With your code, i get This QueryDict instance is immutable error
It's a standard to use the PUT request method for updating in your current code and situation it is better to use the ProfileSerializer since it has more fields. Also, the way I see it you only have three fields in the User object that can be changed and those three fields I assume is rarely changed.
views.py
def put(self, request, *args, **kwargs):
"""
update a profile
"""
data = request.data
print data
try:
profile = Profile.objects.get(token=data.get('token'))
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer = self.serializer_class(profile, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status.HTTP_200_OK)
else:
return Response(serializer.errors,
status.HTTP_400_BAD_REQUEST)
serializers.py
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Profile
fields = ('user', 'height', 'weight', 'token')
def to_internal_value(self, data):
first_name = data.pop('first_name', None)
last_name = data.pop('last_name', None)
data = super(ProfileSerializer, self).to_internal_value(data)
data['first_name'] = first_name
data['last_name'] = last_name
return data
def update(self, instance, validated_data):
first_name = validated_data.pop('first_name', None)
last_name = validated_data.pop('last_name', None)
user_inst_fields = {}
if first_name:
user_inst_fields['first_name'] = first_name
if last_name:
user_inst_fields['last_name'] = last_name
if user_inst_fields:
User.objects.update_or_create(pk=instance.user.id,
defaults=user_inst_fields)
profile, created = Profile.objects.update_or_create(
token=instance.token, defaults=validated_data)
return profile
Simply just set the user to read only for it to avoid validation

django rest framework url arguments with patch not working

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')

Categories