Dealing with unique constraints in nested serializers.

Xavier Ordoquy
Django REST framework
2 min readSep 21, 2016

This question comes pretty often on Stackoverflow /IRC / mailing list.

The issue is when one uses nested serializers while the nested model has a unique constraint. Here’s a Task model that has a foreign key to a User:

Let’s build a serializer that nests a User serializer:

Now -assuming we already have an admin user - if we create the following data:

{
"owner": {
"username": "admin"
},
"name": “demo”
}

we’re catching a unicity error:

HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
"owner": {
"username": [
"A user with that username already exists."
]
}
}

Django REST framework adds a unicity constraint based on the model’s introspection. If we print the serializer, here’s what we’ll see:

TaskSerializer():
id = IntegerField(label='ID', read_only=True)
owner = UserSerializer():
username = CharField(
help_text='...', max_length=150,
validators=[
<django.contrib.auth.validators.UnicodeUsernameValidator object>, <UniqueValidator(queryset=User.objects.all())>
]
)
name = CharField(max_length=128)

As you may notice, there’s a UniqueSerializer on the owner’s username.

Since this is a nested serializer, there’s no way Django REST framework can know whether this object will be created or taken from the database. The create/update actions apply to the TaskSerializer, not to the UserSerializer. Therefore the UniqueValidator will be applied for every request since we may either create the User or update an existing one.

If we want to regain the control over it, we need to explicitly remove that validator:

From now on, the unicity over Username will not be checked during validation. It’ll be up to us to deal with it whenever required — ie when we’ll need to create the nested resource.

--

--

Xavier Ordoquy
Django REST framework

Django and Python coach, Freelance since 2004, part time developer, architect and admin.