Skip to main content

Writable nested helpers for django-rest-framework's serializers

Project description

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:

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(max_length=100)
    profile = models.ForeignKey(Profile, related_name='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.

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:

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

Authors

2014-2020, beda.software

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

drf-writable-nested-0.6.1.tar.gz (9.3 kB view details)

Uploaded Source

Built Distribution

drf_writable_nested-0.6.1-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

Details for the file drf-writable-nested-0.6.1.tar.gz.

File metadata

  • Download URL: drf-writable-nested-0.6.1.tar.gz
  • Upload date:
  • Size: 9.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.0.0 requests-toolbelt/0.9.1 tqdm/4.45.0 CPython/3.7.7

File hashes

Hashes for drf-writable-nested-0.6.1.tar.gz
Algorithm Hash digest
SHA256 3eac83ca663a8752ada27c320beb91d4c0d40aa2e4d9f915f502faae27dc9937
MD5 70de887d7938135d1c47b45296ca4440
BLAKE2b-256 86784f439f28dc9fde0eae14300f3352501d0cbe8cbfe9066dfaec1cb92d1afd

See more details on using hashes here.

File details

Details for the file drf_writable_nested-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: drf_writable_nested-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 8.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.0.0 requests-toolbelt/0.9.1 tqdm/4.45.0 CPython/3.7.7

File hashes

Hashes for drf_writable_nested-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2770274a0aba4123155840d9004a9e7dde208fa82d4f0165b0a19d559bd36211
MD5 fc5b82309874fc83bd848a81493059a2
BLAKE2b-256 7ae9c94bb4eb6603fe4a93ce2b8d34d191e40d987139d96d7ad631aecf526b1a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page