how to use check_password inside validate function? - django-rest-framework

I have this serializer:
class ChangePasswordSerializer(DynamicFieldsModelSerializer):
current = serializers.CharField()
confirm = serializers.CharField()
class Meta:
model = models.User
fields = ('password', 'current', 'confirm')
validators = []
def update(self, instance, validated_data):
if instance.check_password(validated_data.get('current')):
instance.set_password(validated_data['password'])
instance.save()
else:
raise serializers.ValidationError("Current password is not correct")
return instance
I have this inside my update function:
instance.check_password(validated_data.get('current'))
but I want this action happens outside of function update in validate_current function, but I dont know how

You can write custom validator for the current password
def validate_current(self, value):
if not self.instance.check_password(value):
raise serializers.ValidationError("Current password is not correct")
return value
you will have to pass instance from view in serializer

Related

I have to create serializer instance without data since i am not accepting any data from user

Here i am trying to create serializer instance without data argument because all i want to create a "Like" object which requires "user" object which i can get from request and "post" object that i am getting through querying Post model with pk but since i am not passing any data argument while deserializing it will throw an error.
So how can create instance without passing data argument or do i have to change my code and add data argument?
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="likes")
liked_by = models.ForeignKey(User, on_delete=models.RESTRICT, related_name = "liked_posts")
def __str__(self):
return "{0} liked by {1}".format(self.post.img, self.liked_by.username)
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
class AddLike(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
post = Post.objects.get(pk=pk)
user = request.user
serializer = LikeSerializer()
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save(post=post, liked_by=user)
return Response(data= serializer.data, status=status.HTTP_201_CREATED)
I think you can set the post field as read_only in the LikeSerializer.
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
extra_kwargs = {
'post': { 'read_only': True }
}

passwords is changing for all users in django rest

I have made an API for password change but it's changing the passwords for all users instead of only one user.
seriealizer code is below:
class ChangePasswordSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
old_password = serializers.CharField(write_only=True, required=True)
class Meta:
model = User
fields = ('old_password', 'password1', 'password2')
def validate(self, attrs):
if attrs['password1'] != attrs['password2']:
raise serializers.ValidationError({"password": "Password fields didn't match."})
return attrs
def validate_old_password(self, value):
user = self.context['request'].user
if not user.check_password(value):
raise serializers.ValidationError({"old_password": "Old password is not correct"})
return value
def update(self, instance, validated_data):
instance.set_password(validated_data['password1'])
instance.save()
return instance
view code is below:
class ChangePasswordView(generics.UpdateAPIView):
queryset = User.objects.all()
permission_classes = (IsAuthenticated,)
serializer_class = ChangePasswordSerializer
what is wrong with this code ?
Password change is very straight forward. Django already has a form to do it. Try the below code:
#api_view(['PUT'])
#permission_classes([IsAuthenticated])
def change_password(request):
form = PasswordChangeForm(request.user, request.data)
if form.is_valid():
form.save()
serializer = UserSerializer(request.user)
return Response(serializer.data)
return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)
Read this page for more information on how to build user auth methods using DRF: https://kushgoyal.com/creating-a-sceure-login-api-using-drf-token-auth/
url for this will be of this format:
url(r'change_password/', views.change_password)

Djangorestframework, how can I use a serializer with custom fields that I want passed for the creation method?

Let's say I have the following:
class EntityManager(Manager):
def create(label, entity_type, **kwargs):
... do stuff with label and entity type
obj = super().create(**cleanedupkwargs)
obj.addstuffwithlabel(label)
return obj
class Entity(Model):
somefields...
objects = EntityManager()
There's no problem with this and I can call Entity.objects.create(label='foo', entity_type=my_entity_type, other_params=foo)
the issue is I'm now using a serializer and I tried this
class EntityBareboneSerializer(serializers.ModelSerializer):
label = serializers.SerializerMethodField()
entity_type = serializers.SerializerMethodField()
class Meta:
model = Entity
fields = [
'id',
'label',
'entity_type',
]
def validate_label(self, label):
return label
def validate_entity_type(self, entity_type):
return entity_type
def create(self, validated_data):
# do stuff with label and entity type
return Entity.objects.create(**validated_data)
The issue is when is_valid is called the validated_data param comes back empty.
Any idea if it's possible to effectively use my custom create method in the serializer?
You can pre-process the validated data, before creating an instance
def create(self, validated_data):
label = validated_data.pop("label", "some_default_value")
entity_type = validated_data.pop("entity_type", "some_default_value")
obj = Entity.objects.create(**validated_data)
obj.addstuffwithlabel(label)
return obj

clean django api in serilizer

i write a django api i would like to know if reminder field changed then the Appointment model object save current user.
i used this link
See object changes in post_save in django rest framework
and write this code
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
reminder_change = False
def perform_update(self, serializer):
if 'reminder' in serializer.validated_data:
self.reminder_change = True
serializer.save()
def update(self, request, *args, **kwargs):
super(AppointmentBackOfficeViewSet, self).update(request, *args, **kwargs)
instance = self.get_object()
instance.user = request.user
if self.reminder_change:
instance.reminder_user = request.user
instance.save()
res = self.write_serializer_class(instance).data
return Response(res)
class AppointmentCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
exclude = ['created_time', 'modified_time']
is there a way to write this code clearer than this :
dont use self.reminder_change class field is there better way?
may be move this lines to serializer??(in serializer dont access to request.user)
Removed unecessary fields and update method:
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
def perform_update(self, serializer):
# Here you can set attributes directly to the serializer update method
# It's like setting attribute directly to the updating model
# Check that reminder has been changed
reminder = serializer.validated_data.get('reminder')
if reminder and reminder != instance.reminder: # Reminder is different
serializer.save(user=self.request.user,
reminder_user=self.request.user)
else:
serializer.save(user=self.request.user)
This solution will work depending on what type of reminder field is.
If it's String or Integer it will be ok. Problem is that if it's object. Viewset will raise error because serializer reminder field would be integer but instance.reminder would be instance of the reminder object so keep this in mind.

DRF - Foreign key in a non-model serializer

I have a non-model serializer which looks like this:
class NonModelSerializer(Serializer):
secret_number = IntegerField()
user = ???
def save(**kwargs):
... do something with the secret number and user ...
What shall be written instead of ??? so that my serializer accepts ID of a user and in save() method, I see the user of the given ID in the user field? Something like ModelChoiceField from plain Django.
you should use PrimaryKeyRelatedField,:
class NonModelSerializer(serializers.Serializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
secret_number = serializers.IntegerField()
class Meta:
fields = ('user', 'secret_number')
def create(self, validated_data):
print(self.validated_data)
pass
def update(self, instance, validated_data):
pass
I suggest you override create and update instead of save, but you can access the selected user in save by self.validated_data too.

Resources