Reputation: 3212
I have a serializer that is used in a couple of endpoints (generics.ListAPIView), but in one of them I need to hide a serializer field. I would prefer to not write a new serializer just to cover this case.
Starting from DRF 3.0 we have dynamic fields for serializers (https://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields), but I'm having some troubles to fully understand how to use them.
I created this serializer:
class TestSerializer(DynamicFieldsModelSerializer):
user_req = UserSerializer(read_only=True)
user_tar = UserSerializer(read_only=True)
class Meta:
model = TestAssoc
fields = ("user_req", "user_tar")
and this is my endpoint:
class TestEndpointListAPIView(generics.ListAPIView):
serializer_class = TestSerializer
permission_classes = [IsAuthenticated]
lookup_field = 'test_username'
def get_queryset(self):
return ...
Now I need to hide the 'user_tar' field from the output, and according to the documentation I should instantiate the serializer with something like:
TestSerializer(fields=('user_req'))
but how should I do this inside my TestEndpointListAPIView? Should I override get_serializer?
Thanks for the help
EDIT:
I've found the following solution, by overriding the get_serialized function:
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
kwargs['fields'] = ['user_req']
return serializer_class(*args, **kwargs)
I'd like to know if it is a good solution. Thanks!
Upvotes: 4
Views: 2078
Reputation: 408
Add this piece of code to __init__
method of the serializer class as suggested in the DRF docs:
class TestSerializer(serializers.ModelSerializer):
user_req = UserSerializer(read_only=True)
user_tar = UserSerializer(read_only=True)
class Meta:
model = TestAssoc
fields = ("user_req", "user_tar")
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(TestSerializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
And so when you call the serializer from views.py do this :
TestSerializer(queryset, fields=('user_req'))
Alternatively what you can do is define a class
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
if fields is not None:
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
Now import this class if you have defined it in some other file and then inherit it using
class TestSerializer(DynamicFieldsModelSerializer):
This way:
class TestSerializer(DynamicFieldsModelSerializer):
user_req = UserSerializer(read_only=True)
user_tar = UserSerializer(read_only=True)
class Meta:
model = TestAssoc
fields = ("user_req", "user_tar")
Now you can do
TestSerializer(queryset, fields=('user_req'))
Update
In the views. Take an example of ListAPIView
class DemoView(ListAPIView):
queryset = TestAssoc.objects.all()
def get(self, request):
try:
queryset = self.get_queryset()
data = TestSerializer(queryset, fields=('user_req')).data
return Response( {"data" : data } ,status=status.HTTP_200_OK)
except Exception as error:
return Response( { "error" : str(error) } , status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Upvotes: 3