drf-writable-nested

by beda-software

beda-software / drf-writable-nested

Writable nested model serializer for Django REST Framework

529 Stars 78 Forks Last release: 7 months ago (v0.6.0) Other 149 Commits 17 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

DRF Writable Nested

Build Status codecov pypi pyversions

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)

Requirements

  • Python (3.5, 3.6, 3.7, 3.8)
  • Django (2.2, 3.0)
  • djangorestframework (3.8+)

Installation

pip install drf-writable-nested

Usage

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) access_key = models.ForeignKey(AccessKey, null=True)

class Avatar(models.Model): image = models.CharField(maxlength=100) profile = models.ForeignKey(Profile, relatedname='avatars') ```

We should create the following list of serializers:

from rest_framework import serializers
from drf_writable_nested.serializers import WritableNestedModelSerializer


class 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

NestedCreateMixin
or
NestedUpdateMixin
from 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

save
method. These
kwargs
must 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.

Known problems with solutions

Validation problem for nested serializers with unique fields on update

We have a special mixin

UniqueFieldsMixin
which 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

Example of usage:
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:

UniqueFieldsMixin
must be applied only on serializer which has unique fields.
Mixin ordering

When you are using both mixins (

UniqueFieldsMixin
and
NestedCreateMixin
or
NestedUpdateMixin
) you should put
UniqueFieldsMixin
ahead.

For example:

python
class ChildSerializer(UniqueFieldsMixin, NestedUpdateMixin,
        serializers.ModelSerializer):

Authors

2014-2020, beda.software

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.