Jeremy Satterfield
Coding, Making and Tulsa Life

Abusing Django Rest Framework Part 4: Object-level field exclusion

Similar to the object-level readonly field from my previous post, there are some cases where we want to exclude certain fields based on what object the user is trying to access. You could overwrite the views get_serializer method to use a different serializer based on their access, but if nesting serializers is a possiblility this get messy, somewhere in the neighborhood of O(n2). Another option is to modify a serializers to_native method.

class SampleSerializer(serializers.ModelSerializer):
    ...

    def to_native(self, obj):
        restricted = not obj.review_set.filter(
            client__plan__account_details=True).exists()

        if restricted:
            self.fields.pop('billing_address', None)
        return super(SampleSerializer, self).to_native(obj)

The to_native method is responsible for turning the Python object into a simple, serializable value. This means it has to be called for every object that it being retrieved, so it will always be able to affect your output, even when nested.

One drawback is that it only works in conjunction with retrieve views. If the field is defined in the serializer at any point as writeable, the users can still update that field, even if they can't see it. This can be a pretty big security risk when used with update views.

Another Approach

Having implemented this method several months ago, based on a spec that was several months old at the time, I would likely approach the root issue from a different angle if it came up again. Maybe creating updating the spec to have a consistant api response and return blank values for the unauthorized fields would be a better approach. SerializerMthodField has the potential to make this approach much cleaner.