How create nested object in drf with validation?

I want to create nested objects with django-rest-framework serializers(drf).

Initially, I create such serializers:

class CollectionCreateSerializer(ModelSerializer):
   citizens = CitizenSerializer(many=True)

   ## Some definition of serializer fields


    def create(self, validated_data):
        collection = Collection.objects.create()
        citizens = validated_data.pop('citizens')

        for citizen in citizens:
            citizen_serializer = CitizenSerializer(data=citizen,
                                                   context={'citizens': citizens)
            citizen_serializer.is_valid(raise_exception=True)
            citizen_serializer.save()

        return collection

class CitizenSerializer(serializers.ModelSerializer):


   ## Some defenition of serializer fields



    def validate(self, attrs):
        print('in validate citizen')
        citizens = self.context['citizens']

        ## Some validation logic with citizens context

        return super().validate(attrs)

But that did not work, because validate method calls from create method, and before it when validation of collection happens. And the problem is that in the first case context contains 'request' and some other info. And in the second case context contains defined in create method context.

So validate method on each citizen called twice with different context info.

Then I tried to delete is_valid method from create method. And the next logical error occurred:

 You must call `.is_valid()` before calling `.save()`.

Then I tried to make citizens value real-only to prevent internal validation. But then the citizens field is not included in the validated data in the create method.

So I expect that some flag that turns off the internal nested objects validation? Or maybe a nicer solution exists.

Upvotes: 1

Views: 1401

Answers (1)

cagrias
cagrias

Reputation: 1847

The problem in your implementation is that you are creating a CitizenSerializer instance in your create method of your CollectionCreateSerializer.

You shouldn't do that because CitizenSerializer is defined as a nested-serializer field and therefore it is called and validated already when your CollectionCreateSerializer instance is created. See this example for detail.

You need to update your CollectionCreateSerializer such as:

class CollectionCreateSerializer(ModelSerializer):
   citizens = CitizenSerializer(many=True)

   ## Some definition of serializer fields


    def create(self, validated_data):
        collection = Collection.objects.create()

        citizens = validated_data.pop('citizens')
        for citizen in citizens:
            Citizen.objects.create(collection=collection, **citizen)

        return collection

Upvotes: 2

Related Questions