Django Rest Framework - Exclude field from related object - django-rest-framework

I have two related models and serializers for both of them. When I am serializing one of these models (the serializer has a depth of 1) the result includes some fields from the related object that should't be visible. How an I specify which serializer to use for the relation? Or is there anyway to tell Rest Framework to exclude some fields from the related object?
Thank you,

I think one way would be to create an extra serializer for the model where you want to return only limited number of fields and then use this serializer in the serializer of the other model. Something like this:
class MyModelSerializerLimited(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('field1', 'field2') #fields that you want to display
Then in the other serializer use the MyModelSerializerLimited:
class OtherModelSerializer(serializers.ModelSerializer):
myfield = MyModelSerializerLimited()
class Meta:
model = OtherModel
fields = ('myfield', ...)
depth = 1

You could override restore_fields method on serializer. Here in restore_fields method you can modify list of fields - serializer.fields - pop, push or modify any of the fields.
eg: Field workspace is read_only when action is not 'create'
class MyPostSerializer(ModelSerializer):
def restore_fields(self, data, files):
if (self.context.get('view').action != 'create'):
self.fields.get('workspace').read_only=True
return super(MyPostSerializer, self).restore_fields(data, files)
class Meta:
model = MyPost
fields = ('id', 'name', 'workspace')

Related

Create objects with nested serializer by using the UUID

I have a serializer like this:
class SureveyResponseSerializer(serializers.ModelSerializer):
respondent_profile = RespondentsProfileSerializer(read_only=True)
def create(self, validated_data):
... some stuff ...
return survey_response
class Meta:
model = SurveyResponse
fields = '__all__'
My problem here is dealing with creating and reading objects using the same serializer. For reading, I want to show the nested instance of respondent_profile with all its fields.
For a SurveyResponse (and adding a relation to an existing respondent_profile) I want to simply pass the UUID of an existing respondent_profile.
Is this possible or do I need two different serializers?

Django REST Framework: "NoneType object is not iterable" error when trying to use serializer.data construct from within Serializer Method Field?

I am using a model that consists of many fields. There is one field that is a property, and it returns an instance of a model. Something like the following:
class A(Model):
#property
def last_obj(self):
# Returns an object
The issue I'm having is that this property can return 2 different Model types. It can either return an object of type one, or an object of type two. This creates complications in the serializer. I have a serializer that consists of nested serializers. The two objects are similar enough that one serializer can be used over the other, but then the fields unique to them are not serialized.
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
last_obj = SerializerOne() #This works, but not viable because of what I stated above
So my solution to be able to dynamically call the correct serializer, was to conditionally serialize the property within a serializer method field:
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
def get_last_obj(self, instance):
if (isinstance(instance.last_obj, models.one)):
return self.SerializerOne(instance.last_obj).data
else:
return self.SerializerTwo(instance.last_obj).data
last_obj = SerializerMethodField() #Does not work
However, this solution creates the error "NoneType Object is not iterable" and it happens at
super(ReturnDict, self).__init__(*args, **kwargs) in rest_framework/utils/serializers_helpers.py in init which causes the error at return ReturnDict(ret, serializer=self) in rest_framework/serializers.py in data
I do not understand why calling a nested serializer like obj = Serializer() works, but calling the serializer explicitly like obj = Serializer(instance).data does not work in this situation. Can anyone figure out what I have been doing wrong? Thank you.
I have found out from here that when working with hyperlinked relations (which in my case was the CustomSerializer that SerializerOne and SerializerTwo were inheriting from), you must pass the request object through context. The reason why obj = Serializer() works, but obj = Serializer(instance).data does not work is that in the former, the request object is automatically added through context through DRF. While in the latter, it is being explicitly called so you must pass context with the request object manually. So for me to get it working, I did:
return self.SerializerOne(instance.last_obj, context={'request': self.context['request']}).data
inside the serializer method field.

DRF: What's the difference between Field and Serializer

We can use serializer as a field inside another serializer..
Wonder why there's a Field class and Serializer class in DRF?
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
example serializer is taken from the doc https://www.django-rest-framework.org/api-guide/serializers/
As you can see, UserSerializer is much like a Field .
I'm just curious why they have serializer and field class separately..
Serilaizer is infact a field in DRF. Serializers can be nested and that is why it can be used as a field in other serializers. And yes, if you check the source code, the BaseSerializer is a subclass of Field as the serializer is just a special case of a field.
In my opinion:
In django rest framwork, you can think Serializer like a mask. It cover your origin data and change it to anything you want. Like format your json data , or validate your input data have correct format or not.
In your example,
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Comment have 2 direct field type CharField and DateTimeField.
user = UserSerializer()
Comment have field type is UserSerializer. This is different Serializer, and django know your CommentSerializer will have relationship with UserSerializer. And anything define in UserSerializer will use in here, for format json output or validate. And with define this nested objects, your output json will have more data like
'user': {'email': 'foobar', 'username': 'doe'}
And if you want create Comment with one user, you must pass all validate define in UserSerializer.
Conclude: in this example
Field class use for direct field.
Serializer class for relationship with other class

django rest framework ordering non model field(serializer field)

i want to ordering on my fields like this:
class DealerBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
filter_backends = (filters.OrderingFilter,
)
ordering_fields = ('online',...)
this way of ordering work only on model's fields but online field defined in my serializer and while test in postman not work.
i want to done it like this :
class CustomOrdering(filters.OrderingFilter):
def filter_queryset(self, request, queryset, view):
params = request.query_params.get(self.ordering_param)
if params == 'online':
... my serializer codes
return super(CustomOrdering, self).filter_queryset(request, queryset, view)
this problem is other fields ordering not work!! is there a way to solve it any way?
if related docs help me please give me the link .
thanks for your site
after struggle in this challenge i undrestand that exist a way to some how indicate this fields as model field and not need to CustomOrdering and any extra codes!
in my get_queryset function i change the code:
queryset = Dealer.objects.all()
to:
queryset = Dealer.objects.all().annotate(bids_count=Count('bid'), device_count=Count('device'))
note that this two fields in my serializer not in my model.
in my serilizer change this two field from SerializerMethodField to IntegerField and clean the defs.
then in my api file add this:
filter_backends = (filters.OrderingFilter,)
ordering_fields = ('bids_count', 'device_count')
this my last serializer:
class DealerListSerializer(serializers.ModelSerializer):
device_count = serializers.IntegerField()
bids_count = serializers.IntegerField()
class Meta:
model = Dealer
fields = ('id', 'last_name', 'first_name', 'username', 'person_trust', 'is_active',
'work_type', 'address', 'mobile', 'device_count', 'online', 'bids_count')
by this way my code is very clear and my CustomOrdering and all elif statements also clean!
It doesn't work because the fields defined in your serializer aren't part of the model. The ordering attribute only works for model fields. You'd probably have to introduce a work around like creating a dynamic field using annotations and then order using that field but this depends on whether or not your online field logic can be annotated.

Django rest framework. Deserialize json fields to different fields on the model

I have a json response from a web request which almost maps to my django model.
How do I serialize this json(preferably with a model serializer),but override one field, so I can map it to a differently named field on the Django model. (I have one field "expected_value" in the json object, but I want to map that to the "actual_value" of my Django model).
You can add extra fields to a ModelSerializer or override the default fields by declaring fields on the class, just as you would for a Serializer class.
Something like the code snippet below should work.
class MySerializer(serializers.ModelSerializer):
expected = serializers.Field(source='actual')
class Meta:
model = MyModel
fields = ('field1', 'field2', 'expected')

Resources