context in nested serializers django rest framework
Ok i found a working solution. I replaced the ChildSerializer assignment in the Parent class with a SerializerMethodField which adds the context. This is then passed to the get_fields method in my CustomModelSerializer:
class ChildSerializer(CustomModelSerializer): class Meta: fields = ('c_name', ) model = Childclass ParentSerializer(CustomModelSerializer): child = serializers.SerializerMethodField('get_child_serializer') class Meta: model = Parent fields = ('p_name', 'child') def get_child_serializer(self, obj): serializer_context = {'request': self.context.get('request') } children = Child.objects.all().filter(parent=obj) serializer = ChildSerializer(children, many=True, context=serializer_context) return serializer.data
and in my CustomModelSerializer:
class CustomModelSerializer(rest_serializer_classes.HyperlinkedModelSerializer): def __init__(self, *args, **kwargs): """ Make sure a user is coupled to the serializer (needed for permissions) """ super().__init__(*args, **kwargs) if not self.context: self._context = getattr(self.Meta, 'context', {}) try: self.user = self.context['request'].user except KeyError: self.user = None def get_fields(self): ret = OrderedDict() if not self.user: print("No user associated with object") return ret fields = super().get_fields() # Bypass permission if superuser if self.user.is_superuser: return fields for f in fields: if has_right(self.user, self.Meta.model.__name__.lower(), f, "read"): ret[f] = fields[f] return ret
This seems to work fine, and fields of the child are discarded in the serializer when i either revoke read-rights on Child.c_name or on Parent.child
If you can not change the nature of you child serializer, as in @Kirill Cherepanov and @Robin van Leeuwen answers, a light but not full-integrated solution would be to manually pass the context in __init__()
function :
class ChildSerializer(CustomModelSerializer): class Meta: fields = ('c_name', ) model = Childclass ParentSerializer(CustomModelSerializer): child = ChildSerializer(many=True, read_only=True) class Meta: model = Parent fields = ('p_name', 'child') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # We pass the "upper serializer" context to the "nested one" self.fields['child'].context.update(self.context)
You can use serialziers.ListField
instead. ListField
automatically passes context to it's child. So, here's your code
class ChildSerializer(ModelSerializer): class Meta: fields = ('c_name', ) model = Childclass ParentSerializer(ModelSerializer): child = serializers.ListField(read_only=True, child=ChildSerializer()) class Meta: model = Parent fields = ('p_name', 'child')