I’d like to have a model serializer in which some fields are only displayed if the
request.user in the serializer
context is a staff member.
I know I could override the
__init__ method and do something like:
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.context["request"].user.is_staff: self.fields["staff_only_field1"] = serializers.BooleanField() self.fields["staff_only_field2"] = serializers.CharField()
However, the issue with this is that I have to manually initialize each field, thus losing the handy ability of model serializers to automatically instantiate the fields that have the same name as the corresponding model’s fields.
What I’d like to be able to do is just dynamically extend the initial list of fields conditionally, and then have the serializer take care of the rest during initialization.
This is the closest I’ve come to it:
class MySerializer(serializers.ModelSerializer): class Meta: model = SomeModel fields = ["public_field1", "public_field2"] staff_only_fields = ["staff_only_field1", "staff_only_field2"] def __init__(self, *args, **kwargs): self.Meta.fields.extend(self.Meta.staff_only_fields) super().__init__(*args, **kwargs) if not self.context["request"].user.is_staff: for field in self.Meta.staff_only_fields: self.fields.pop(field)
The good thing about this approach is it achieves what I want—the serializer doesn’t have to know the type of the fields and if I want to change anything about them I just need to edit the two lists in
The bad thing is that this is a little too contrived (hacky?) for my liking: I first add all the private fields to the
field list, then call
__init__, and then, if the user isn’t a staff member, I pop them back off. The problem is essentially that I have no access to
self.context until I have
init'ed the serializer. At which point, it’s too late to work on
Meta.fields because an
OrderedDict with all the actual fields has been created.
Is there anything more elegant (and possibly efficient) that I can do to achieve this?