0

I'm trying to create two model instances from a single post request. Model2 is linked to Model1 using a ForeignKey field. Model1 also has a validate() in its serializer. The issue is when I use nested serializer to save both model instances together, the validate function of Model1 returns an error AttributeError: 'Model1Serializer' object has no attribute 'initial_data'.

As per DRF docs

If the data keyword argument is not passed then the .initial_data attribute will not exist.

Trials: I tried intercepting the flow in __init__() and validate() of CombinedSerializer but it seems, the validate() of Model1Serializer is evaluated first.

Doubt: Why is the Model1Serializer not getting supplied with the data keyword argument and how can I correct my code? At what stage is the Model1Serializer validate() function evaluated?

Models:

from django.db import models

class Model1(models.Model):
    key1 = models.PositiveIntegerField(blank=False, null=False)

    def __str__(self):
        return self.key1

class Model2(models.Model):
    foreign_key = models.ForeignKey('Model1', blank=True, null=True, on_delete=models.CASCADE)
    key2 = models.CharField(max_length=20)

    def __str__(self):
        return self.key2

Serializers:

from rest_framework import serializers
from .models import Model1, Model2

class Model1Serializer(serializers.ModelSerializer):
    def validate(self, data):
         if(self.initial_data['key1']>=10):
            data['key1'] = self.initial_data['key1'] + 1
            return data
         else:
            data['key1'] = self.initial_data['key1']
            return data
      
    class Meta:
        model = Model1
        fields = '__all__'

class Model2Serializer(serializers.ModelSerializer):
    class Meta:
        model = Model2
        fields = '__all__'

class CombinedModelSerializer(serializers.ModelSerializer):
    model1 = Model1Serializer()
    model2 = Model2Serializer()

    def create(self, validated_data):
        model2_data = validated_data.pop('model2')
        model1 = Model1.objects.create(**validated_data)
        model2 = Model2.objects.create(foreign_key=model1.id, **model2_data)
        return model1

    class Meta:
        model = Model1
        fields = '__all__'
    

Views:

from django.shortcuts import render
from rest_framework import generics
from .models import Model1, Model2
from .serializers import Model1Serializer, Model2Serializer, CombinedModelSerializer

class Model1List(generics.ListCreateAPIView):
    queryset = Model1.objects.all()
    serializer_class = Model1Serializer

class Model2List(generics.ListCreateAPIView):
    queryset = Model2.objects.all()
    serializer_class = Model2Serializer

class CombinedList(generics.ListCreateAPIView):
    queryset = Model1.objects.all()
    serializer_class = CombinedModelSerializer

Post Request:

POST: http://127.0.0.1:8000/combined/

{
    "model1":
    {
        "key1": 45
    },
    "model2": {
        "key2": "val2"
    }
}

Package Versions:

Python==3.6.8
Django==2.2.4
djangorestframework==3.10.3
2
  • What are you going to validate in Model1Serializer? I don't understand what the current validator is doing. Commented May 19, 2022 at 8:06
  • Its just a sample validator that I've included to simplify the problem. The actual validator includes some business logic working with numbers received in the request and finally modifying the data to save in Model1. Commented May 19, 2022 at 8:16

1 Answer 1

0

initial_data exists only when you pass the data into the constructor of the Model1Serializer. But in views.py, you didn't pass it.

So if you wanna use initial_data, please pass that data. If you don't need them, you need to remove them in validate function body.

Sign up to request clarification or add additional context in comments.

1 Comment

As per a previous doubt that I had asked on StackOverflow, I wanted to keep the view, the thinnest layer, and wanted to achieve the creation of both the Model1 entry and Model2 entry using the CombinedSerializer. Hence I was not using view to achieve this goal. Is there no way possible to achieve this using only the serializer?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.