If i have a nested serializer:
class ChildSerializer(ModelSerializer): class Meta: fields = ('c_name', ) model = Child class ParentSerializer(ModelSerializer): child = ChildSerializer(many=True, read_only=True) class Meta: model = Parent fields = ('p_name', 'child')
And i want to access the context in the nested serializer, how can i do that? As far as i can tell, context isn't passed to the Child.
I want to be able to implement a permission model per user on fields, for that i overridden the get_fields() method of the ModelSerializer:
def get_fields(self): fields = super().get_fields() .... for f in fields: if has_rights(self.context['request'].user, f, "read"): ret_val[f] = fields[f] .... return ret_val
Which works for regular serializers, but the context, and thus the request and user are not available when the nested child is passed to get_fields(). How do i access the context when the serializer is nested?
In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.
Context are used in Django REST Framework when you want to add extra data to the serializer in addition to the object being serialized. views.py. class PostViewSet(ModelViewSet): queryset = Post.
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
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 = Child class 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 = Child class 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With