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"
Related
I'm trying to update user info with a serializer, but even if I don't change the user's username or email when submitting, I get the validating error mention in the title. I just want to update info like the first name for example, but if I change its user name into an already existing one, then I want the error to pop up.
Serializer.py
class UpdateUserSerializer(serializers.ModelSerializer):
class Meta:
model = UserAccount
fields = ['username', 'email', 'first_name', 'last_name', 'about', 'password']
extra_kwargs = {"password": {"write_only": True}}
def validate_password(self, value):
user = self.context['request'].user
if not user.check_password(value):
raise serializers.ValidationError('Incorrect Password')
def update(self, instance, validated_data):
instance.username = validated_data.get("username", instance.username)
instance.email = validated_data.get("email", instance.email)
instance.first_name = validated_data.get("first_name", instance.first_name)
instance.last_name = validated_data.get("last_name", instance.last_name)
instance.about = validated_data.get("about", instance.about)
instance.save()
return instance
Views.py
#api_view(['PATCH'])
#permission_classes([IsAuthenticated])
def update_user(request):
if request.method == 'PATCH':
user = request.user
serializer = UpdateUserSerializer(data=request.data, context={'request': request})
data = {}
if serializer.is_valid():
user = serializer.update(user, request.data)
data['user'] = UserSerializer(user).data
else:
data = serializer.errors
return Response(data)
You have to pass the instance to UpdateUserSerializer when initializing it, otherwise is_valid() will think that you are creating a new object, hence the error.
So you can do something like:
#api_view(['PATCH'])
#permission_classes([IsAuthenticated])
def update_user(request):
if request.method == 'PATCH':
user = request.user
serializer = UpdateUserSerializer(
data=request.data, instance=user, context={'request': request}
)
data = {}
if serializer.is_valid():
user = serializer.save()
data['user'] = UserSerializer(user).data
else:
data = serializer.errors
return Response(data)
save() will call update() for you when there is an instance in the serializer so no need to explicitly call it.
I have UserProfile model that contains phone, profile_photo and some fields of Django default User model like first_name, last_name, email and I am trying to update some of these fields.
models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, verbose_name="User")
phone = models.CharField(max_length=16, verbose_name="Phone")
profile_photo = models.ImageField(null=True, blank=True, upload_to=user_directory_path, verbose_name="Profile Photo")
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('url', 'first_name', 'last_name', 'email')
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(partial=True)
class Meta:
model = UserProfile
fields = '__all__'
def create(self, validated_data):
user_profile = UserProfile.objects.create(**validated_data)
return user_profile
views.py
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
authentication_classes = (TokenAuthentication,)
#detail_route(methods=['PATCH'], url_path='update-partial')
def user_profile_update_partial(self, request, pk=None):
profile = UserProfile.objects.get(id=pk)
serializer = self.get_serializer(profile, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
If I send profile_photo, phone, first_name or last_name data with this #detail_route I am only able to update phone and profile_photo fields. Also getting Bad Request error when profile_photo data not sending.
How can I implement the partial_update with PATCH method?
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
authentication_classes = (TokenAuthentication,)
def partial_update(self, request, *args, **kwargs):
profile = self.get_object()
serializer = self.get_serializer(profile, data=request.data, partial=True)
if serializer.is_valid():
user_serializer = UserSerializer(profile.user, data=request.data, partial=True)
if user_serializer.is_valid():
user_serializer.save()
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
else:
return Response(data=user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(data=serializer.errors,status=status.HTTP_400_BAD_REQUEST)
Q1:How to implement PATCH method?
A1:Override partial_update method.
Q2:How to update first_name or last_name?
A2:Update it with another Serializer named UserSerializer.(If you want update password,you need use make_password method to create encoded password before save raw password to database)
I am trying to return otp to my response alongwith other serializer data. but as it is not submitted through request i am unable to understand how to give it in response. OTP is getting stored with other data in the user table perfectly but I am facing problem only in returning it in response .my code is given below:
models.py
class User(AbstractUser):
phone_number = models.IntegerField(null=True)
otp = models.IntegerField(null=True)
serializers.py
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
password_confirmation = serializers.CharField(read_only=True)
class Meta:
model = User
fields = ('username', 'password', 'password_confirmation', 'email', 'first_name', 'last_name', 'phone_number')
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
instance.otp = randint(1, 98787)
instance.save()
return instance
def otp(self):
instance = self.Meta.model()
return instance.otp
views.py
#api_view(['POST'])
def user_signup(request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
content = {'user': serializer.data, 'otp': UserSerializer.otp()}
return Response(content, status=status.HTTP_201_CREATED)
Since serializer.save() return instance you can do following:
if serializer.is_valid():
instance = serializer.save()
content = {'user': serializer.data, 'otp': instance .otp}
So def otp(self) in UserSerializer is not required.
I need the code for adding user image to the inbuilt User model in django rest framework. I do need the code for serializers, views and settings for directory of image
I tried almost everything but completely unable to add it. my code for user registration is given but there is no models.py as I am using inbuilt User model
Views.py
class SignUp(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(content, status=status.HTTP_201_CREATED)
Serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(write_only=True)
password_confirmation = serializers.CharField(read_only=True)
username = serializers.CharField(allow_null=False)
email = serializers.EmailField(allow_null=False)
first_name = serializers.CharField(allow_null=False)
last_name = serializers.CharField(allow_null=False)
class Meta:
model = User
fields = ('id', 'username', 'password', 'password_confirmation', 'email', 'first_name', 'last_name')
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
If you prefer a simple way, then it would be relating an Image model with every User through a OneToOne Relationship,
In your models.py,
class Image(models.Model):
user = models.OneToOneField(User)
file = models.ImageField(upload_to='images/')
serializers.py,
class UserSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(write_only=True)
password_confirmation = serializers.CharField(read_only=True)
avatar = serializers.ImageField(source='image.file')
class Meta:
model = User
fields = ('id', 'username', 'password', 'password_confirmation',
'email', 'first_name', 'last_name', 'avatar',
)
def create(self, validated_data):
password = validated_data.pop('password', None)
# print(validated_data)
image = validated_data.pop('image')
print(image)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
if image is not None:
obj = Image(user=instance, **image)
obj.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
elif attr == 'image':
image = Image.objects.get(user=instance)
image.file = validated_data.get('image')['file']
image.save()
else:
setattr(instance, attr, value)
instance.save()
return instance
views.py,
class HomeView(APIView):
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
serializer = UserSerializer(data=request.data)
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)
Adding a field to the in built User model would be an unnecessary workaround. If you could manage to keep the additional attributes independent from the in built models, it'd be more reliable in case if refactoring ever needed.
in your models you can use FileField or ImageField
upload = models.FileField(upload_to='path/')
photo = models.ImageField(upload_to='path')
for more detais about fields in django models you can refer to this_link
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"
}