Reputation: 3383
I, as a newbie Django developer, am trying to build a RESTful API for a mobile app. I've took over an existing project and previous developers have used Django REST Framework. Super cool package, easy to work with so far. Except for one thing...
There is this problem when I want to create new resources, which happen to have nested serializers. I'm not great on explaining software issues with words, so here is the simplified version of my case:
class UserSerializer(serializers.ModelSerializer):
company = CompanySerializer()
# other props and functions are unrelated
class CompanySerializer(serializers.ModelSerializer):
# props and functions are unrelated
Now with this structure, GET /users
and GET /users/{id}
endpoints work great and I get the results I expect. But with POST /users
and PATCH /users/{id}
I get a response that says I need to provide an object for company
prop and it should resemble a Company object with all the required props, so that it can create the company too. And I'm sure it tries to create a new company because I've tried sending { company: { id: 1 } }
and it simply ignores the ID and requires a name
to create a new one. This is obviously not what I want because I just want to create a user (who may or may not belong to a company), not both a user and a company.
I've tried switching that CompanySerializer
to a serializers.PrimaryKeyRelatedField
and it seems like it works on create endpoint but now I don't get the Company object on list and detail endpoints.
What am I missing here? I'm 99% sure that they did not intend this framework to work this way.
Upvotes: 0
Views: 943
Reputation: 15105
You need to override create()
and update()
methods on nested serializers to make them writable. Otherwise DRF is not sure what to do with nested objects. The simplest override would go something like this:
class UserSerializer(serializers.ModelSerializer):
company = CompanySerializer()
...
def create(self, validated_data):
return User.objects.create(**validated_data)
def update(self, instance, validated_data):
user = instance
user.__dict__.update(validated_data)
user.save()
return user
Note: haven't tested this variant of update()
might need adjustments.
Upvotes: 1
Reputation: 599628
The trick is to use a different serializer class for retrieving vs updating - one with a PrimaryKeyRelatedField and one with a nested serializer. You can override get_serializer_class
to do this. Assuming you are using a viewset:
class BaseUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (...)
class WriteUserSerializer(BaseUserSerializer):
company = CompanySerializer()
class ReadUserSerializer(BaseUserSerializer):
company = PrimaryKeyRelatedField()
class UserViewSet(viewsets.ViewSet):
def get_serializer_class(self):
if self.action in ["create", "update", "partial_update", "destroy"]:
return WriteUserSerializer
else:
return ReadUserSerializer
Upvotes: 0