How to get user object from request in django rest framework? - python

I want to get the password of curent user from request body and verify it.
I am trying to get user object from request like this:
class UserPasswordUpdateAsAdminViewSet(APIView):
def patch(self, request, pk):
serializer = ResetPasswordAsAdminSerializer(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.update_password()
return Response(status=status.HTTP_200_OK, data={'success': True})
and below is my serializer:
class ResetPasswordAsAdminSerializer(serializers.ModelSerializer):
new_password = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
def validate_password(self, attrs):
self.user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
self.user = request.user
if not self.user or not self.user.check_password(attrs['password']):
raise CustomAPIException(
status_code=status.HTTP_401_UNAUTHORIZED,
message='Invalid password provided',
error_code=None
)
def validate_new_password(self, password):
if not re.match(REGEX['password'], str(password)):
raise CustomAPIException(
status_code=status.HTTP_409_CONFLICT,
message=None,
error_code='password_policy_mismatch'
)
return password
def update_password(self):
self.user.set_password(self.validated_data['new_password'])
self.user.save()
return
class Meta:
model = User
fields = ('password', 'new_password')
But on hitting the api, I am getting the following error:
string indices must be integers
What am I doing wrong?

You don't have to do this in the serializer itself, it can be done in the views pretty easily take a look at this example
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
if not user.check_password(serializer.data.get('old_password')):
return Response({'old_password': ['Wrong password.']},
status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
user.set_password(serializer.data.get('new_password'))
user.save()
return Response({'status': 'password set'}, status=status.HTTP_200_OK)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)

Related

How to write a login view for my register view in rest frame work?

I'm a beginner in Django and the rest framework and I'm trying to write a class-based login view with the rest framework for my register view please help me for writing a login class-based view what is important is view be class-based with rest
this is a registered view of my project and then its serializer at the bottom of that
class RegisterView(GenericAPIView):
serializer_class = UserSerializer
permission_classes = (permissions.AllowAny,)
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
user_data = serializer.data
user = User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
print(current_site)
# relativeLink = reverse('verify-email')
# print(type(relativeLink))
absurl = 'http://' + current_site + "?token=" + str(token)
email_body = 'سلام' + user.username + '\nبرای فعال سازی حساب خود وارد لینک زیر شوید' + '\n' \
+ absurl
data = {'email_body': email_body, 'to_email': user.email, 'email_subject': 'Verify your email'}
Util.send_email(data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
it is a register view serializer in serializer.py
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length=65, min_length=8, write_only=True)
confirm_password = serializers.CharField(
max_length=65, min_length=8, write_only=True)
def validate_email(self, value):
lower_email = value.lower()
if User.objects.filter(email__iexact=lower_email).exists():
raise serializers.ValidationError("ایمیل تکراری است")
return lower_email
def validate(self, data):
if not data.get('password') or not data.get('confirm_password'):
raise serializers.ValidationError("لطفا پسورد را وارد و تایید کنید ")
if data.get('password') != data.get('confirm_password'):
raise serializers.ValidationError("پسورد اشتباه است")
return data
class Meta:
model = User
fields = ['username', 'email', 'password', 'confirm_password'
]
write_only_fields = ('password', 'repeat_password')
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.set_password(validated_data['confirm_password'])
user.save()
return user
class LoginView(APIView):
def post(self, request):
username = request.data['username']
password = request.data['password']
# use a method to get access token (from the package you are using)
# access token class will return access token if the user is authenticated
# otherwise it will return error response
pass

DRF Custom Update password got KeyError: 'request'

When I want to update password received error,
user = self.context['request'].user
KeyError: 'request'
Can someone help me with it?
serializer:
Custom serializer
class UserPasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True)
password = serializers.CharField(required=True)
class Meta:
model = User
fields = ('old_password', 'password')
def validate_old_password(self, data):
user = self.context['request'].user
if not user.check_password(data):
raise serializers.ValidationError(
{'old_password': 'Wrong password.'}
)
return data
def update(self, instance, validated_data):
instance.set_password(validated_data['password'])
return super().update(instance)
My action view
My action view
#action(methods=['patch'], detail=True)
def change_password(self, request, *args, **kwargs):
user = self.get_object()
user.serializer = UserPasswordChangeSerializer(data=request.data)
user.serializer.is_valid(raise_exception=True)
user.serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)
You need to pass request as context variable to the serializer
#action(methods=['patch'], detail=True)
def change_password(self, request, *args, **kwargs):
user = self.get_object()
user.serializer = UserPasswordChangeSerializer(data=request.data,
context={'request': request})
user.serializer.is_valid(raise_exception=True)
user.serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)
Reference: Including extra context in Serializer

Django rest serializer validation errors during update

I have a separate serializer for updating a user's account, and for some reason whenever I try to use an invalid input its not sending the validation errors as a response, its only sending back the original values that were set. Eg. old username is abc123, if I try to update it to abc123* i want it to throw an error saying its not a proper format but instead it just sends back abc123 as serializer.data. Anybody know why this is happening?
serializer
class UpdateAccountSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=16)
full_name = serializers.CharField(max_length=50)
class Meta:
model = Account
fields = ['username', 'full_name']
def validate_username(self, username):
if Account.objects.filter(username=username).exists():
raise serializers.ValidationError(_("This username is taken."))
if not re.fullmatch(r'^[a-zA-Z0-9_]+$', username):
raise serializers.ValidationError(
_("Usernames must be alphanumeric, and can only include _ as special characters."))
return username
def validate_full_name(self, full_name):
if not re.fullmatch(r'^[a-zA-Z ]+$', full_name):
raise serializers.ValidationError(
_("Invalid name."))
return full_name
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.full_name = validated_data.get(
'full_name', instance.full_name)
instance.save()
return instance
view
class UpdateAccountView(APIView):
def patch(self, request, pk, format=None):
account = Account.objects.filter(id=pk)
if account.exists():
account = account[0]
if request.user == account:
serializer = UpdateAccountSerializer(
account, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer._errors, status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_404_NOT_FOUND)
If you're wondering why I'm using separate serializers for registration and updates, its because these fields should be the only ones users are allowed to update freely.
My best guess is its because of these lines (below), but I can't find a fix.
instance.username = validated_data.get('username', instance.username)
instance.full_name = validated_data.get('full_name', instance.full_name)
Your possible solution is to raise-exception. change this lines
if serializer.is_valid():
serializer.save()
to
if serializer.is_valid(raise_exception=True):
serializer.save()
And also don't need to overwrite update method in serializer.
In validated_data.get('username', instance.username) line we are doing from validate_data dictionary object get username if its not present then set this value which is instance.username. Btw one more thing patch request you should response with status code 200-ok not 201-created.

Django update api

I ma creating a user profile update api via django:
in urls:
url(r'^/api/users/(?P<user_id>[0-9]+)$', UserView.as_view(), name='user_profile'),
And my view:
class UserView(APIView):
def patch(self, request, user_id):
# logging.info('user Id: %s' % user_id)
logging.info('in patch...')
user = User.objects.get(id=user_id)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
why patch def not called at all?! (I get 504 method not allowed)
my request is:
patch > http://localhost:8000/api/users/2
When i'm deleting the user_id argument in view, it works, but i need to get the user id in path.
def patch(self, request, user_id):
# logging.info('user Id: %s' % user_id)
logging.info('in patch...')
user = User.objects.get(id=2)
serializer = UserSerializer(instance=user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
You must provide instance eq: serializer = UserSerializer(instance=user, data=request.data, partial=True)
try this.
class UserView(APIView):
def patch(self, request, *args, **kwargs):
# try to get user_id from kwargs.get('user_id', None)

Django RestFrame work to update the fied

My serializer fun is
class change_pwdform(serializers.ModelSerializer):
def update(self, instance, validated_data):
user.set_password(validated_data.get('new_password', new_password))
user.save()
return instance
old_password = serializers.CharField( style={'input_type': 'password'})
new_password = serializers.CharField( style={'input_type': 'password'})
class Meta:
fields = ( 'pty_email','old_password','new_password',)
model = registration
in my model i have only the filed called
password field in model
my view function to change password is,
class Change_Password(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
serializer_class = change_pwdform
def post(self, request, format=None):
email = request.data['pty_email']
pwd = request.data['old_password']
user = registration.objects.filter(pty_email=email, pty_password=pwd)
if user:
user = registration.objects.get(pty_email=email)
request['password'] = request.data['new_password']
print request.data //says quickdict of email , password , new_password, and old_password
serializer = change_pwdform(object, data=request.DATA, partial=True)
if serializer.is_valid():
serializer.save()
data = { "result":"success"}
return Response(data)
error ={"Invalid"}
return Response(error, status=status.HTTP_400_BAD_REQUEST)
This request return a dict ack: success but the password is not yet changed.
Thanks in advance..
You can wright an update function in your serializers like this.Following
def update(self, instance, validated_data):
user.set_password(validated_data.get('new_password', new_password))
user.save()
return instance
Here user is the user object and in your views.py you have to pass the model object like this
change_pwdform = change_pwdform(object, data=request.DATA, partial=True)
This worked for me and you can also refer "http://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-update"

Categories

Resources