I am using Django Rest to create a simple API.I need to create a view where the user can change his/hers password.I am using the default Django user model and a simple UserSerializer. There is method called set_password but i cant find a way the use it corrently with the user seriliazer.I cant find any solution anywhere.
UserSelializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', "username", 'email', 'first_name', 'last_name', 'password')
View(ClassedBased): Here is an example (i have no idea what i am doing here):
class UserChangePassword(APIView):
def patch(self, request):
user = self.request.user
serialized = UserSerializer(data=request.DATA)
if serialized.is_valid():
user.set_password(serialized.data['password'])
user.save()
return Response(status=status.HTTP_205_RESET_CONTENT)
else:
return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)
Mind that i want to post a json script to change to password. Something like this :
{
"old_password": "123",
"new_password": "12345"
}
One method could be to override the restore_object method in the Serializer. This would look something like this:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', "username", 'email', 'first_name', 'last_name', 'password')
# turn text to hashed password
def restore_object(self, attrs, instance=None):
attrs['password'] = make_password(attrs['password'])
return super(UserSerializer, self).restore_object(attrs, instance=None)
Now, when this deserializes into an object instance, you will have a valid hashed password. You should then be able to accomplish what you desire by modifying the view you currently have by just a bit.
class UserChangePassword(APIView):
def patch(self, request):
serialized = UserSerializer(data=request.DATA)
if serialized.is_valid():
serialized.save()
return Response(status=status.HTTP_205_RESET_CONTENT)
else:
return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)
And I believe the JSON in your PATCH request would (depending on the look up type, which I think defaults to id) look something like this:
{
"id": "83",
"password": "12345"
}
I hope this helps!
EDIT:
Please note that as Symmetric has pointed out in the comments, restore_object has been deprecated in DRF 3.0
Password reset using Viewset
In View
from rest_framework.decorators import detail_route, list_route, permission_classes
from rest_framework import viewsets
class UserProfileViewSet(viewsets.ViewSet):
permission_classes = (AllowAny,)
serializer_class = UserProfileSerializer
def list(self, request):
queryset = UserProfile.objects.all()
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data)
def create(self, request):
serializer = self.serializer_class(data=request.data)
# check email address is exists or not.
user_type = request.data['user_type']
user_token = register_by_social(request.data['email'], request.data['username'], user_type)
if not user_token or user_token == True:
if not User.objects.filter(Q(email=request.data['email'])
| Q(username=request.data['username'])).exists():
if serializer.is_valid():
userprofile = serializer.save()
return Response({
'status': status.HTTP_201_CREATED,
'message': 'Successfully signup new user.',
'token': userprofile.user.auth_token.key })
return Response({
'status': status.HTTP_400_BAD_REQUEST,
'message': 'Please provided required fields.',
'error' : serializer.errors })
return Response({
'status': status.HTTP_409_CONFLICT,
'message': 'Email address or username is already exists.'})
return Response({
'status': status.HTTP_200_OK,
'message': 'Social user is already registered.',
'token': user_token })
#list_route(permission_classes=[IsAuthenticated], authentication_classes = (BasicAuthentication, TokenAuthentication),
methods=['post'], url_path='reset-user-password')
def reset_user_password(self, request, pk=None):
reset_password_serializer = UserResetPasswordSerializer(request.user, data=request.data)
if reset_password_serializer.is_valid():
if not request.user.check_password(request.data.get('password')):
return Response({"password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)
request.user.set_password(request.data.get('new_password'))
request.user.save()
return Response({"Message": ["Password reset successfully"]}, status=status.HTTP_200_OK)
You can make a serializer only for password in serializer.py
import django.contrib.auth.password_validation as validators
class UserResetPasswordSerializer(serializers.ModelSerializer):
password = serializers.CharField(source='user.password', style={'input_type': 'password'},
max_length=20, min_length=8)
new_password = serializers.CharField(style={'input_type': 'password'},
max_length=20, min_length=8)
class Meta:
model = User
fields =("password", 'new_password')
Your question is answered at: https://stackoverflow.com/a/27586192/2950621
Don't do anything about passwords in the view class. Add set_password calls in overridden create and update methods on the UserSerializer class.
Your patch (partial update) view can be created using ModelViewSet like:
class UserViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = User.objects.all()
serializer_class = serializers.UserSerializer
As per #DRC in the cited answer, add create and update methods on the UserSerializer class:
def create(self, validated_data):
user = get_user_model(**validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
for f in UserSerializer.Meta.fields + UserSerializer.Meta.write_only_fields:
set_attr(instance, f, validated_data[f])
instance.set_password(validated_data['password'])
instance.save()
return instance
Also, your incoming JSON should look more like:
{
"username": "mariodev",
"password": "12345"
}
Related
Currently having difficulties returning a token in my registration view.
How Can I fix this so that UserSerializer's create can return a token and username?
View.py
#permission_classes([AllowAny])
class UserCreate(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
Serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
user = User(**validated_data)
user.set_password(password)
user.save()
token, _ = Token.objects.get_or_create(user=user)
return Response(
{
'username': validated_data['username'],
'token': token.key,
},
status=HTTP_200_OK
)
Error
AttributeError: Got AttributeError when attempting to get a value for field `username` on serializer `UserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Response` instance.
Original exception text was: 'Response' object has no attribute 'username'.
[13/Nov/2020 20:26:44] "POST /account/register HTTP/1.1" 500 120116
Ended up putting it in the Serializer:
class UserCreate(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key, 'username': serializer.instance.username}, status=HTTP_201_CREATED)```
I was trying to register user in django using User model from 'django.contrib.auth.models', but when encrypting password, it shows 'The crypt module is not supported on Windows'. Also I tried importing from passlib.hash the pbkdf2_sha256 to encrypt the data.
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
encryptedpass = pbkdf2_sha256.encrypt(validated_data['password'], rounds=12000, salt_size=32)
user = User.objects.create_user(username=validated_data['username'], email=validated_data['email'], password=encryptedpass)
return user
views.py
class RegisterAPI(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class LoginAPI(KnoxLoginView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
encryptedpass = pbkdf2_sha256.encrypt(request.data['password'], rounds=12000, salt_size=32)
mydata = {'username':request.data['username'], 'password':encryptedpass}
serializer = AuthTokenSerializer(data=mydata)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginAPI, self).post(request, format=None)
# Get User API
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
But during authentication in login part, it does not match the credentials, maybe because the function checking for validity uses some other encryption method. How to solve this issue.
I have a working register API in my DRF backend. I want to be able to perform updates on user profiles with PUT requests. How can I write a method that validates the user and updates the user with the matching id? I also want write a method that handles the DELETE requests and deletes the user with the id matching the request.
# serializers.py
class RegisterSerializer(ModelSerializer):
class Meta:
model = User
fields = (
'account_type',
'id',
'username',
'name',
'email',
'phone',
'country',
'city',
'adress_1',
'adress_2',
'zip_code',
'photo',
'password',
)
...
def create(self, validated_data):
user = super(RegisterSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
# api.py
class RegisterAPI(CreateAPIView):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
token = Token.objects.create(user=serializer.instance)
token_data = {"token": token.key}
return Response({
"token": token.key,
"user": {**serializer.data}
})
# urls.py
urlpatterns = [
...
path('api/auth/register', RegisterAPI.as_view()),
...
]
You can read about put method more detailed information in the docs
def put(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I have a problem with PATCH Null Value in Django Rest Framework with Extend User Model. Please take a look my issues!
Serializers:, Profile is extend User Model
class UserEditSerializer(ModelSerializer):
job_title = serializers.CharField(source='profile.job_title')
class Meta:
model = User
fields = [
'username',
'job_title',
]
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.save()
if (validated_data.get('profile') is not None):
profile_data = validated_data.pop('profile')
profile = instance.profile
profile.job_title = profile_data.get('job_title', profile.job_title)
My viewsets:
class UserUpdateAPIView(ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserEditSerializer
permission_classes = (IsAuthenticated,)
#detail_route(methods=['PATCH'])
def edit(self, request):
user_obj = User.objects.get(id=request.user.id)
serializer = UserEditSerializer(user_obj, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return JsonResponse({'message': 'Error'}, status=500)
My api patch request to server:
{
"job_title": ""
}
Error:
{
"detail": "Method \"GET\" not allowed."
}
Error Photo
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"