Writable nested model serializer for Django REST Framework
This is a writable nested model serializer for Django REST Framework which allows you to create/update your models with related nested data.
The following relations are supported: - OneToOne (direct/reverse) - ForeignKey (direct/reverse) - ManyToMany (direct/reverse excluding m2m relations with through model) - GenericRelation (this is always only reverse)
pip install drf-writable-nested
For example, for the following model structure: ```python from django.db import models
class Site(models.Model): url = models.CharField(max_length=100)
class User(models.Model): username = models.CharField(max_length=100)
class AccessKey(models.Model): key = models.CharField(max_length=100)
class Profile(models.Model): sites = models.ManyToManyField(Site) user = models.OneToOneField(User, ondelete=models.CASCADE) accesskey = models.ForeignKey(AccessKey, null=True, on_delete=models.CASCADE)
class Avatar(models.Model): image = models.CharField(maxlength=100) profile = models.ForeignKey(Profile, relatedname='avatars', on_delete=models.CASCADE) ```
We should create the following list of serializers:
from rest_framework import serializers from drf_writable_nested.serializers import WritableNestedModelSerializerclass AvatarSerializer(serializers.ModelSerializer): image = serializers.CharField()
class Meta: model = Avatar fields = ('pk', 'image',)
class SiteSerializer(serializers.ModelSerializer): url = serializers.CharField()
class Meta: model = Site fields = ('pk', 'url',)
class AccessKeySerializer(serializers.ModelSerializer):
class Meta: model = AccessKey fields = ('pk', 'key',)
class ProfileSerializer(WritableNestedModelSerializer): # Direct ManyToMany relation sites = SiteSerializer(many=True)
# Reverse FK relation avatars = AvatarSerializer(many=True) # Direct FK relation access_key = AccessKeySerializer(allow_null=True) class Meta: model = Profile fields = ('pk', 'sites', 'avatars', 'access_key',)
class UserSerializer(WritableNestedModelSerializer): # Reverse OneToOne relation profile = ProfileSerializer()
class Meta: model = User fields = ('pk', 'profile', 'username',)
Also, you can use
NestedCreateMixinor
NestedUpdateMixinfrom this package if you want to support only create or update logic.
For example, we can pass the following data with related nested fields to our main serializer:
data = { 'username': 'test', 'profile': { 'access_key': { 'key': 'key', }, 'sites': [ { 'url': 'http://google.com', }, { 'url': 'http://yahoo.com', }, ], 'avatars': [ { 'image': 'image-1.png', }, { 'image': 'image-2.png', }, ], }, }user_serializer = UserSerializer(data=data) user_serializer.is_valid(raise_exception=True) user = user_serializer.save()
This serializer will automatically create all nested relations and we receive a complete instance with filled data.
python user_serializer = UserSerializer(instance=user) print(user_serializer.data)
{ 'pk': 1, 'username': 'test', 'profile': { 'pk': 1, 'access_key': { 'pk': 1, 'key': 'key' }, 'sites': [ { 'pk': 1, 'url': 'http://google.com', }, { 'pk': 2, 'url': 'http://yahoo.com', }, ], 'avatars': [ { 'pk': 1, 'image': 'image-1.png', }, { 'pk': 2, 'image': 'image-2.png', }, ], }, }
It is also possible to pass through values to nested serializers from the call to the base serializer's
savemethod. These
kwargsmust be of type
dict. E g:
# user_serializer created with 'data' as above user = user_serializer.save( profile={ 'access_key': {'key': 'key2'}, }, ) print(user.profile.access_key.key)
'key2'
Note: The same value will be used for all nested instances like default value but with higher priority.
To run unit tests, run: ```bash
python3 -m venv envname source envname/bin/activate
pip install django pip install django-rest-framework pip install -r requirements.txt
py.test ```
We have a special mixin
UniqueFieldsMixinwhich solves this problem. The mixin moves
UniqueValidator's from the validation stage to the save stage.
If you want more details, you can read related issues and articles: https://github.com/beda-software/drf-writable-nested/issues/1 http://www.django-rest-framework.org/api-guide/validators/#updating-nested-serializers
class Child(models.Model): field = models.CharField(unique=True)class Parent(models.Model): child = models.ForeignKey('Child')
class ChildSerializer(UniqueFieldsMixin, serializers.ModelSerializer): class Meta: model = Child
class ParentSerializer(NestedUpdateMixin, serializers.ModelSerializer): child = ChildSerializer()
class Meta: model = Parent
Note:
UniqueFieldsMixinmust be applied only on serializer which has unique fields.
When you are using both mixins (
UniqueFieldsMixinand
NestedCreateMixinor
NestedUpdateMixin) you should put
UniqueFieldsMixinahead.
For example:
python class ChildSerializer(UniqueFieldsMixin, NestedUpdateMixin, serializers.ModelSerializer):
2014-2021, beda.software