Gurkenkönig
Gurkenkönig

Reputation: 828

Password required Django REST API User Serializer PUT request

I a the problem that in my Django Rest API User Serializer: The password field is necessary when making a PUT request. Within a POST request this makes sense, but with PUT is especially if PUT is performed by an administrator, the password field should allowed to be empty.

How can I change my serializer so that the password is necessary for POST, but empty for PUT?

class UserSerializer(serializers.ModelSerializer):
    """The UserSerializer"""
    gender = serializers.IntegerField(source='profile.gender', read_only=False)
    clinic = serializers.CharField(
        source='profile.clinic.code', read_only=False)
    title_prefix = serializers.CharField(
        source='profile.academic_title_prefix',
        allow_blank=True,
        read_only=False)
    title_suffix = serializers.CharField(
        source='profile.academic_title_suffix',
        allow_blank=True,
        read_only=False)

    email = serializers.EmailField(
        required=True,
        validators=[UniqueValidator(queryset=User.objects.all())])
    username = serializers.CharField(
        validators=[UniqueValidator(queryset=User.objects.all())])
    password = serializers.CharField(min_length=8, write_only=True)

    class Meta:
        model = User
        fields = ('id', 'url', 'username', 'first_name', 'last_name', 'gender',
                  'title_prefix', 'title_suffix', 'clinic', 'email',
                  'is_staff', 'is_superuser', 'date_joined', 'last_login',
                  'password')


    def create(self, validated_data):
        """Create and return a new user and its associated profile."""

        user = User.objects.create_user(
            validated_data['username'],
            validated_data['email'],
            validated_data['password'],
        )

        user.set_password(validated_data['password'])

        user.first_name = validated_data['first_name']
        user.last_name = validated_data['last_name']
        user.is_staff = validated_data['is_staff']
        user.is_superuser = validated_data['is_superuser']

        user.save()

        # create associated profile
        profile_data = validated_data.pop('profile')

        profile = Profile.objects.create(
            user=user,
            gender=profile_data['gender'],
            clinic=Clinic.objects.get(code=profile_data['clinic']['code']),
            academic_title_prefix=profile_data['academic_title_prefix'],
            academic_title_suffix=profile_data['academic_title_suffix'],
        )
        user.profile = profile

        return user

    def update(self, instance, validated_data):
        """Update and return a existing user and its associated profile."""

        instance.first_name = validated_data.get('first_name',
                                                 instance.first_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)

        # Only Superuser can make Superusers
        if self.context['request'].user.is_superuser:
            instance.is_staff = validated_data.get('is_staff',
                                                   instance.is_staff)
            instance.is_superuser = validated_data.get('is_superuser',
                                                       instance.is_superuser)

        profile_data = validated_data.pop('profile')

        profile = Profile.objects.get(user=instance)
        profile.gender = profile_data['gender']
        profile.clinic = Clinic.objects.get(
            code=profile_data['clinic']['code'])
        profile.academic_title_prefix = profile_data['academic_title_prefix']
        profile.academic_title_suffix = profile_data['academic_title_suffix']
        profile.save()
        instance.profile = profile

        return instance

Upvotes: 1

Views: 1380

Answers (2)

ruddra
ruddra

Reputation: 51988

If you are using PUT to update the values and only want to ommit validation for password field, then you can try like this for viewset and generic views:

class UserSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        super(UserSerializer, self).__init__(*args, **kwargs)
        if self.context['request'].method == "PUT":
            self.fields.pop('password')

   # rest of the code

Upvotes: 4

mon io
mon io

Reputation: 752

It's not problem of the serializer. Problem is that DRF requires all fields using PUT method. Method that don't require all fields is PATCH.

You need to override update method in viewset:

def update(self, request, *args, **kwargs):
    partial = True # Here I change partial to True
    instance = self.get_object()
    serializer = self.get_serializer(instance, data=request.data, partial=partial)
    serializer.is_valid(raise_exception=True)
    self.perform_update(serializer)

    return Response(serializer.data)

Upvotes: 5

Related Questions