Consider a Listing model that has an associated Category. I want to create a new Listing for an existing Category by doing a POST with data: 
{"title": "myapp", "category": {"name": "Business"}}, where title is the title of the Listing that should be created, and Business is the name of an existing category to use for this new listing.
When I try to make such a request and instantiate the ListingSerializer for this, I get an error indicating that the Category name must be unique - I don't want to create a new Category, but use an existing one instead. I've tried setting the validators on the category field to [], but that didn't change the behavior.
I can use a SlugRelatedField, but that forces my request data to look more like {"title": "myapp", "category": "Business"}, which isn't what I want. I tried using the source argument for the SlugRelatedField to specify a nested relationship, but that didn't work either:
category = serializers.SlugRelatedField(
        slug_field='category.name',
        queryset=models.Category.objects.all()
    )
yields:
  "category": [
    "Object with name={'name': 'Business'} does not exist."
  ]
models.py:
import django.contrib.auth
from django.db import models
from django.conf import settings
class Profile(models.Model):
    display_name = models.CharField(max_length=255)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
class Category(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=200)
class Listing(models.Model):
    title = models.CharField(max_length=50, unique=True)
    category = models.ForeignKey(Category, related_name='listings', null=True)
    owners = models.ManyToManyField(
        Profile,
        related_name='owned_listings',
        db_table='profile_listing',
        blank=True
    )
serializers.py:
import logging
import django.contrib.auth
from rest_framework import serializers
import myapp.models as models
logger = logging.getLogger('mylogger')
class ShortUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = django.contrib.auth.models.User
        fields = ('username', 'email')
class ProfileSerializer(serializers.ModelSerializer):
    user = ShortUserSerializer()
    class Meta:
        model = models.Profile
        fields = ('user', 'display_name')
        read_only = ('display_name',)
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = ('name', 'description')
        read_only = ('description',)
class ListingSerializer(serializers.ModelSerializer):
    owners = ProfileSerializer(required=False, many=True)
    # TODO: how to indicate that this should look for an existing category?
    category = CategorySerializer(required=False, validators=[])
    class Meta:
        model = models.Listing
        depth = 2
    def validate(self, data):
        logger.info('inside ListingSerializer validate')
        return data
    def create(self, validated_data):
        logger.info('inside ListingSerializer.create')
        # not even getting this far...
views.py:
import logging
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
import django.contrib.auth
from rest_framework import viewsets
from rest_framework.response import Response
import myapp.serializers as serializers
import myapp.models as models
# Get an instance of a logger
logger = logging.getLogger('mylogger')
class CategoryViewSet(viewsets.ModelViewSet):
    queryset = models.Category.objects.all()
    serializer_class = serializers.CategorySerializer
class UserViewSet(viewsets.ModelViewSet):
    queryset = django.contrib.auth.models.User.objects.all()
    serializer_class = serializers.ShortUserSerializer
class ProfileViewSet(viewsets.ModelViewSet):
    queryset = models.Profile.objects.all()
    serializer_class = serializers.ProfileSerializer
class ListingViewSet(viewsets.ModelViewSet):
    logger.info('inside ListingSerializerViewSet')
    queryset = models.Listing.objects.all()
    serializer_class = serializers.ListingSerializer
Full example: https://github.com/arw180/drf-example
This isn't ideal, but I did find a solution that solved my problem (I'm waiting to accept it as the answer, hoping someone else can do better). There are two parts:
First, use the partial=True argument when initializing the ListingSerializer ( http://www.django-rest-framework.org/api-guide/serializers/#partial-updates). Then use the serializer's validate method to get the actual model instance corresponding to the input data.
Second, explicitly remove the validators for the name field in the CategorySerializer. This is especially lousy because it effects more than just the ListingSerializer. 
Leaving out either piece will result in the validation errors being thrown at the time the serializer is instantiated.
modifications to views.py:
class ListingViewSet(viewsets.ModelViewSet):
    queryset = models.Listing.objects.all()
    serializer_class = serializers.ListingSerializer
    def create(self, request):
        serializer = serializers.ListingSerializer(data=request.data,
            context={'request': request}, partial=True)
        if not serializer.is_valid():
            logger.error('%s' % serializer.errors)
            return Response(serializer.errors,
                  status=status.HTTP_400_BAD_REQUEST)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
modifications to serializers.py:
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = ('name', 'description')
        read_only = ('description',)
        # also need to explicitly remove validators for `name` field
        extra_kwargs = {
            'name': {
                'validators': []
            }
        }
class ListingSerializer(serializers.ModelSerializer):
    owners = ProfileSerializer(required=False, many=True)
    category = CategorySerializer(required=False)
    class Meta:
        model = models.Listing
        depth = 2
    def validate(self, data):
        # manually get the Category instance from the input data
        data['category'] = models.Category.objects.get(name=data['category']['name'])
        return data
    def create(self, validated_data):
        title = validated_data['title']
        listing = models.Listing(title=validated_data['title'],
            category=validated_data['category'])
        listing.save()
        if 'owners' in validated_data:
            logger.debug('owners: %s' % validated_data['owners'])
            for owner in validated_data['owners']:
                print ('adding owner: %s' % owner)
                listing.owners.add(owner)
        return listing
I'll wait a bit to accept this as the answer in case someone can come up with a better solution (like how to make the source argument work properly with a SlugRelatedField) - I have a working example using the solution above at https://github.com/arw180/drf-example if you want to experiment. I'd also love to hear comments regarding why the extra_kwargs stuff is necessary in the CategorySerializer - why isn't instantiating it like this: category = CategorySerializer(required=False, validators=[]) sufficient (in the ListingSerializer)? UPDATE: I believe that doesn't work because the unique validator is added automatically from the DB constraints and run regardless of any explicit validators set here, as explained in this answer: http://iswwwup.com/t/3bf20dfabe1f/python-order-of-serializer-validation-in-django-rest-framework.html
CategorySerializer.create into an update_or_create method on nameclass CategorySerializer(serializers.ModelSerializer):
    ...
    # update_or_create on `name`
    def create(self, validated_data):
        try:
            self.instance = Category.objects.get(name=validated_data['name'])
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
            return self.instance
        except Category.DoesNotExist:
            return super(CategorySerializer, self).create(validated_data)
    ...
I recommend looking at the DRF source when ever you need to create custom functionality.
Related question answered by the creator of DRF: django-rest-framework 3.0 create or update in nested serializer
So I was still in the DRF 2 mindset where nested writable fields are handled automatically. You can read up on the subject here: http://www.django-rest-framework.org/topics/3.0-announcement/
I've tested the following code and it works:
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        ...
        extra_kwargs = {
            'name': {'validators': []},
            'description': {'required': False},
        }
class ListingSerializer(serializers.ModelSerializer):
    ...
    def update_or_create_category(self, validated_data):
        data = validated_data.pop('category', None)
        if not data:
            return None
        category, created = models.Category.objects.update_or_create(
            name=data.pop('name'), defaults=data)
        validated_data['category'] = category
    def create(self, validated_data):
        self.update_or_create_category(validated_data)
        return super(ListingSerializer, self).create(validated_data)
    def update(self, instance, validated_data):
        self.update_or_create_category(validated_data)
        return super(ListingSerializer, self).update(instance, validated_data)
The correct way of using SlugRelatedField is like this, in case you were wondering:
class ListingSerializer(serializers.ModelSerializer):
    ...
    # slug_field should be 'name', i.e. the name of the field on the related model
    category = serializers.SlugRelatedField(slug_field='name',
        queryset=models.Category.objects.all())
    ...
                        I had similar problem: I needed to check if nested serializer (CategorySerializer)  exists if yes to use it and if not - create it from nesting serializer (ListingSerializer). The solution of @demux totally worked for me only if I didn't use custom validation for a field in nested serializer (the field by which I would check from nesting serializer if this instance exists). So I added create() method to nested serializer and  @demux custom update_or_create_category(), create(), update() for ListingSerializer worked perfectly.
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
    model = Category
    ...
    def create(self, validated_data):
        if Category.objects.filter(name=self.validated_data['name']).exists():
            raise serializers.ValidationError("This category name already exists")
        return Category.objects.create(**validated_data)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With