DRF tries to create User object when used in nested serializer
DRF docs says that "By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create create() and/or update() methods in order to explicitly specify how the child relationships should be save."<< In general this is true except for one case. When User class is a nested object in serializer. Use case is to add existing user to newly created organization.
Consider my simple example where I noticed that issue:
views.py
class UserOrganizationViewSet(viewsets.ModelViewSet):
# authentication_classes = (JwtAuthentication, SessionAuthentication)
# permission_classes = (permissions.JWT_RESTRICTED_APPLICATION_OR_USER_ACCESS,)
serializer_class = UserOrganizationSerializer
queryset = UserOrganization.objects.all()
models.py:
class UserOrganization(models.Model):
name = models.CharField(max_length=256)
users = models.ManyToManyField(User, blank=True, related_name="user_organizations", through='OrganizationMember')
def __str__(self):
return self.name
class OrganizationMember(models.Model):
organization = models.ForeignKey(UserOrganization, on_delete=CASCADE)
user = models.ForeignKey(User, on_delete=CASCADE)
joined = AutoCreatedField(_('joined'))
serializers.py:
- First version - User class has its own simple serializer and it is used as nested serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ['username']
class UserOrganizationSerializer(serializers.ModelSerializer):
users = UserSerializer(many=True, required=False)
class Meta:
model = UserOrganization
fields = ['id', 'name', 'users']
read_only_fields = ['id']
Is this case I am able to fetch data via GET method (Organization created through admin panel):
But when I am trying to create organization I am getting error which says that user already exist:

So, I checked what will happen when I try to paste not existed username in JSON data send. According to expectation DRF says that I need to override create() method:
{
"name": "Test2",
"users": [
{
"username": "admins"
}
]
}
AssertionError: The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `organization.serializers.UserOrganizationSerializer`, or set `read_only=True` on nested serializer fields.
And there is a first weird behavior. DRF still tries to created User object:

My create method looks like this:
def create(self, validated_data):
print("validated_data", validated_data)
users = validated_data.pop('users', [])
user_organization = UserOrganization.objects.create(**validated_data)
print("users", users)
for user in users:
userSerializer = UserSerializer(user)
print("userSerializer.data", userSerializer.data)
username = userSerializer.data['username']
print("username", username)
user_organization.users.add(get_user_model().objects.get(username=username))
return user_organization
In this case my method isn't called at all.
In case when I passed user which does not exist, my create() method is called and exeption is thrown as it should be. (I know I am not handling errors, this is just sandbox):
{
"name": "Test2",
"users": [
{
"username": "admin1"
}
]
}
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
- New model class UserProfile and serializer for this class used as nested:
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['name']
class UserOrganizationSerializer(serializers.ModelSerializer):
profiles = UserProfileSerializer(many=True)
class Meta:
model = UserOrganization
fields = ['id', 'name', 'profiles']
read_only_fields = ['id']
def create(self, validated_data):
print("validated_data ", validated_data)
users = validated_data.pop('profiles', [])
user_organization = UserOrganization.objects.create(**validated_data)
print("create", users)
for user in users:
print("username = ", user['name'])
user_organization.profiles.add(UserProfile.objects.get(name=user['name']))
user_organization.save()
return user_organization
Small change was needed to change field users to profiles inside UserOrganization.
Creating organization with existed user profile (created by admin) via POST:
In this scenario there is no such problem as mentioned in first case.
- Final solution using SlugReleatedField works as expected. No serializer used for User.
class UserOrganizationSerializer(serializers.ModelSerializer):
users = serializers.SlugRelatedField(many=True, queryset=get_user_model().objects.all(), slug_field='username')
class Meta:
model = UserOrganization
fields = ['id', 'name', 'users']
read_only_fields = ['id']
Above code does what I exactly need and is very simple. Here is small concern regarding performance when fetched all users from DB in this line:
queryset=get_user_model().objects.all()
Can someone explain why this happens what we could observe in scenario 1?
Sorry for this post being so long. I tried to shorten it as much I as can. I can put whole project in github if it will be needed for readability.


