49👍
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
28👍
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)
- [Django]-CSRF Failed: CSRF token missing or incorrect
- [Django]-How to display the current year in a Django template?
- [Django]-Getting 'DatabaseOperations' object has no attribute 'geo_db_type' error when doing a syncdb
10👍
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 = Child
class ParentSerializer(ModelSerializer):
child = serializers.ListField(read_only=True, child=ChildSerializer())
class Meta:
model = Parent
fields = ('p_name', 'child')
- [Django]-Models.py getting huge, what is the best way to break it up?
- [Django]-Django: Implementing a Form within a generic DetailView
- [Django]-Django project models.py versus app models.py
8👍
Ok, I have found an ultimate solution that will do exactly what was asked – pass context down to nested serializers.
To achieve that one need to override to_representation(self, instance)
of the nested serializer, so it looks like:
def to_representation(self, instance):
# here we update current serializer's context (access it as self._context)
# to access parent's context we use parent.context
# if there is no parent than it's the first serializer in the chain and it doesn't need any context except for itself's
# for example (after all the checks)
self._context["request"] = self.parent.context["request"]
# and that is it! The modified context will be used for serialization as if it was passed as usually
return super().to_representation(instance)
- [Django]-Django – comparing old and new field value before saving
- [Django]-Heroku, postgreSQL, django, comments, tastypie: No operator matches the given name and argument type(s). You might need to add explicit type casts
- [Django]-Row level permissions in django
4👍
If you are trying to limit the queryset of the child serializer field, then go ahead and use
self.parent.context
inside the child serializer to access the parent context.
like so:
def get_fields(self):
fields = super().get_fields()
fields['product'].queryset = Product.objects.filter(company=self.parent.context['company'])
return fields
This answer led me to find this via debugging and looking at the available variables in child’s get_fields function.
- [Django]-Django set DateTimeField to database server's current time
- [Django]-What's the difference between `from django.conf import settings` and `import settings` in a Django project
- [Django]-Django required field in model form
3👍
I know this is an old question, but I had the same question in 2019. Here is my solution:
class MyBaseSerializer(serializers.HyperlinkedModelSerializer):
def get_fields(self):
'''
Override get_fields() method to pass context to other serializers of this base class.
If the context contains query param "omit_data" as set to true, omit the "data" field
'''
fields = super().get_fields()
# Cause fields with this same base class to inherit self._context
for field_name in fields:
if isinstance(fields[field_name], serializers.ListSerializer):
if isinstance(fields[field_name].child, MyBaseSerializer):
fields[field_name].child._context = self._context
elif isinstance(fields[field_name], MyBaseSerializer):
fields[field_name]._context = self._context
# Check for "omit_data" in the query params and remove data field if true
if 'request' in self._context:
omit_data = self._context['request'].query_params.get('omit_data', False)
if omit_data and omit_data.lower() in ['true', '1']:
fields.pop('data')
return fields
In the above, I create a serializer base class that overrides get_fields()
and passes self._context
to any child serializer that has the same base class. For ListSerializers, I attach the context to the child of it.
Then, I check for a query param “omit_data” and remove the “data” field if it’s requested.
I hope this is helpful for anybody still looking for answers for this.
- [Django]-How do I access the request object or any other variable in a form's clean() method?
- [Django]-Django : Can't import 'module'. Check that module AppConfig.name is correct
- [Django]-How to make email field unique in model User from contrib.auth in Django
3👍
I am using djangorestframework
3.12.xx and the context is automatically propagated to nested serializers.
- [Django]-Django – How to make a variable available to all templates?
- [Django]-Where should signal handlers live in a django project?
- [Django]-How can I use Bootstrap with Django?
0👍
I read the source code and I believe that it is not necessary to pass the context to nested field serializers as they have access to it.
Calling for the nested field serializer’s context will return the root context. Here is the code and it has not been changed for 9 years.
If you want some explanation, in this example consider field is child
. Calling child.context
will call the above mentioned property function which within itself uses self.root
. root property function will be resolved to ParentSerializer
in this case.
So calling child.context
will result in calling parent.context
.
- [Django]-Check if key exists in a Python dict in Jinja2 templates
- [Django]-How to disable admin-style browsable interface of django-rest-framework?
- [Django]-Why does django run everything twice?
0👍
With newest releases of Django REST Framework, the context
is passed from parent to child serializer without need to override __init__
method, all you have to do is pass context
when you call child serializer:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = ChildModel
fields = ['title']
title = serializers.SerializerMethodField()
def get_title(self, instance):
request = self.context.get('request')
#TODO
return
class ParentSerializer(serializers.ModelSerializer):
class Meta:
model = ParentModel
fields = ['details']
details = serializers.SerializerMethodField()
def get_details(self, instance):
child_instances = ChildModel.objects.all()
return ChildSerializer(
instance=child_instances,
many=True,
read_only=True,
context=self.context
).data
- [Django]-Django: Get an object form the DB, or 'None' if nothing matches
- [Django]-AssertionError: database connection isn't set to UTC
- [Django]-How do I create multiple model instances with Django Rest Framework?