SilentDev
SilentDev

Reputation: 22747

Django Rest Framework - Possible to allow unadmin users to only change one field of the serializer and admin users to change all fields?

This is my serializer:

class LocationSerializer(serializers.ModelSerializer):

    class Meta:
        model = Location
        fields = ('city', 'users', 'locationName', 'locationCode')

    def create(self, validated_data):

        location = Location.objects.create(
                city = validated_data['city'],
                locationName = validated_data['locationName'],
                locationCode = validated_data['locationCode'],
        )

        return location

    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        # Only admin users are allowed to change the location name and code.
        instance.locationName = validated_data.get('locationName', instance.locationName)
        instance.locationCode = validated_data.get('locationCode', instance.locationCode)

        # All users are allowed to add themselves to the 'users' list.
        if validated_data.get('user', None):
            instance.users.add(self.request.user)

        return instance

What's the best way for me to achieve this? (I have commented on the code above mentioning what I want to achieve - I want only admin users to be able to change the location name and code but I want any authenticated user to be able to add themselves to the 'users' list).

Upvotes: 2

Views: 1166

Answers (1)

Roba
Roba

Reputation: 688

Perhaps you may consider the logic where you are using two different serializers, depending on the current user. The decision you can make it in get_serilizer_class, based on the current user and request type (dispatch method name). As such you can separate the view logic from the serializer logic.

Working on your serializer, you may use some view context data to inspect the current user:

class LocationSerializer(serializers.ModelSerializer):

    class Meta:
        model = Location
        fields = ('city', 'users', 'locationName', 'locationCode')

    def create(self, validated_data):

        location = Location.objects.create(
                city = validated_data['city'],
                locationName = validated_data['locationName'],
                locationCode = validated_data['locationCode'],
        )

        return location

    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        # Only admin users are allowed to change the location name and code.
        if self.context['request'].user.is_staff:
            instance.locationName = validated_data.get('locationName', instance.locationName)
            instance.locationCode = validated_data.get('locationCode', instance.locationCode)

        # All users are allowed to add themselves to the 'users' list.
        if validated_data.get('user', None):
            instance.users.add(self.request.user)

        return instance

Simplifying the design, like suggested first:

class AdminLocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ('city', 'users', 'locationName', 'locationCode')


class UserLocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ('city', 'users', 'locationName', 'locationCode')
        read_only_fields = ('locationName', 'locationCode')

class LocationViewSet(generics.ModelViewSet):
    queryset = Location.objects.all()

    def get_serializer_class(self):
        if self.request.user.is_staff or self.action == 'create':
            return AdminLocationSerializer
        else:
            return UserLocationSerializer

Try it. Let me know how it works for you.

Upvotes: 2

Related Questions