Post method to pass data to multiple model serializers

I am new to Django and have a json object whose data has to be split and stored in 3 different Django models. I'm trying to figure out if I'm doing this correctly in using the view file and serializers in Django.

The json object that I receive into the views file looks like below:

[
    {
        "contact_entry": 
        {
            "employee_name" : "Tom Hanks",
            "employee_type" : "Full-time permanent"
        },
        "address" : 
        {
            "line1": "1435 Manhattan Ave",
            "line2": "Apt 123",
            "city": "New York",
            "state": "New York",
            "country":"US",
        },
        "phone_number" : 
        {
            "work_number": "9901948626"
            "home_number": "9908847555"
        }
    }
]

I have three django models as shown below:

class ContactEntry(models.Model):
    employee_name = models.CharField(max_length=128)
    employee_type = models.CharField(max_length=128)
    
class Address(models.Model):
    contact_entry = models.ForeignKey(ContactEntry, on_delete=models.CASCADE)
    line1 = models.CharField(max_length=128)
    line2 = models.CharField(max_length=128)
    city = models.CharField(max_length=128)
    state = models.CharField(max_length=128)
    country = models.CharField(max_length=128)

class PhoneNumber(model.Model):
    contact_entry = models.ForeignKey(ContactEntry, on_delete=models.CASCADE)
    work_number = models.IntegerField(max_length=10)
    home_number = models.IntegerField(max_length=10)

Each of them has a ModelSerializer as shown below:

class ContactEntrySerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactEntry
        fields = '__all__'

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = '__all__'

class PhoneNumberSerializer(serializers.ModelSerializer):
    class Meta:
        model = PhoneNumber
        fields = '__all__'

I have defined one serializer (inherited from serializers.serializer) that combines the above 3 serializers as shown below:

class CombinedSerializer(serializers.Serializer):
    contact_entry = ContactEntrySerializer()
    phone_number = PhoneNumberSerializer()
    address = AddressSerializer()

The post method in my view is as shown below:

class CombinedContactView(APIView):

    def post(self, request, *args, **kwargs):

        # Validate JSON data with the serializer
        serializer = CombinedSerializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data.pop()

        contact_entry_data = validated_data['contact_entry']
        address_data = validated_data['address']
        phone_number_data = validated_data['phone_number']

        # Create model for contact book entry
        contact_entry_model = ContactEntry(**contact_entry_data)
        contact_entry_model.save()

        # Create model for address
        address_data['contact_entry'] = contact_entry_model
        address_model = Address(**address_data)
        address_model.save()

        # Create model for phone_number
        phone_number_data['contact_entry'] = contact_entry_model
        phone_number_model = PhoneNumber(**phone_number_data)
        phone_number_model.save()

        return HttpResponse(status=201)

The above code actually runs fine. I see the contact_entry object created and the address and phone_numbers correctly created (with a foreign key relationship to the contact_entry object). However, I'm concerned that I'm doing this in a roundabout way with lots of unnecessary code in my views file. Is there a more straightforward way to do this?

if you change your serializers to these you'll be OK:

class ContactEntrySerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactEntry
        fields = '__all__'


class AddressSerializer(serializers.ModelSerializer):
    contact_entry = ContactEntrySerializer(required=False)

    class Meta:
        model = Address
        fields = '__all__'


class PhoneNumberSerializer(serializers.ModelSerializer):
    contact_entry = ContactEntrySerializer(required=False)

    class Meta:
        model = PhoneNumber
        fields = '__all__'

just notice that i changed contact_entry on AddressSerializer and PhoneNumberSerializer to required=False so you can pass contact_entry validation and these two serializers. and after it your view do the rest.

Back to Top