Idris Stack
Idris Stack

Reputation: 566

ForeignKey is null after POST in Django Rest (Nested Serializer)

I am a newbie in Django, and I had implemented a one-to-many relationship, however one fieled which is nested is null.

When I post destinations is null in the response section. Below is my response section :

{
    "id": 7,
    "name": "Germany tour",
    "description": "This is a fascinating tour to take",
    "price": "30.0",
    "destinations": null,
    "capacity": 10
}

This is the way I am posting in the Body section in postman :

{
    "name": "Germany tour",
    "description": "This is a fascinating tour to take",
    "price": 30.0,
    "destinations": [
        {
            "location":"Germany",
            "tour_type":"Adventure",
            "danger_type":"Medium"
        }
],
    "capacity": 10
}

Then, below is my models file :

from django.db import models


class Destinations(models.Model):
    location = models.CharField(max_length=20)
    tour_type = models.CharField(max_length=20)
    danger_type = models.CharField(max_length=20)

    class Meta:
        unique_together = ['location', 'tour_type']


class TourPackages(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField()
    price = models.DecimalField(max_digits=9, decimal_places=1)
    destinations = models.ForeignKey(Destinations, related_name="destinations", on_delete=models.CASCADE)
    capacity = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

Then, my serializers file :

from rest_framework import serializers
from .models import TourPackages, Destinations


class DestinationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Destinations
        fields = ['tour_type', 'danger_type']


class TourPackagesSerializer(serializers.ModelSerializer):
    destinations = DestinationSerializer(many=True)

    class Meta:
        model = TourPackages
        fields = ['id', 'name', 'description', 'price', 'destinations', 'capacity']

    def create(self, validated_data):
        destination_data = validated_data.pop("destinations")
        location = TourPackages.objects.create(**validated_data)

        for dest_data in destination_data:
            Destinations.objects.create(location=location, **dest_data)

        return location

Then finally my views file :

from rest_framework import generics

from .models import TourPackages
from .serializer import TourPackagesSerializer


class TourList(generics.ListCreateAPIView):
    queryset = TourPackages.objects.all()
    serializer_class = TourPackagesSerializer


class TourDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = TourPackages.objects.all()
    serializer_class = TourPackagesSerializer

What I want is to post all the data and comes back in response

Upvotes: 2

Views: 1457

Answers (2)

neferpitou
neferpitou

Reputation: 424

You need to create Destinations object first in order to assign them in case of ForeignKey. From the sample data you have posted it implies that you can have multiple destinations and you have on_delete=models.CASCADE set on that. Suppose for a particular tour you have4 destinations and you decide to delete two of them, then models.CASCADE will try to delete your tours object. If this happens where will the remaining 2 destinations object point to? Database integrity will be lost. So Django will not allow this. You can fix by changing ForeignKey To ManyToManyField. And your TourPackagesSerializer should be like this:

Building on the answer from Ananya:

class TourPackagesSerializer(serializers.ModelSerializer):
    destinations = DestinationSerializer(many=True)

    class Meta:
        model = TourPackages
        fields = ['id', 'name', 'description', 'price', 'destinations', 'capacity']

    def create(self, validated_data):
        destination_data = validated_data.pop('destinations')
        dest_obj_list = []
        for dest_data in destination_data:
            dest_obj = Destinations.objects.create(**dest_data)
            dest_obj_list.append(dest_obj)
        location = TourPackages.objects.create(**validated_data)
        location.destinations.set(dest_obj_list)
        location.save()
        return location

Upvotes: 3

Ananya
Ananya

Reputation: 101

In the create method you are creating the destination objects but not setting the Foreign Key in location. Try this:

def create(self, validated_data):
    destination_data = validated_data.pop('destinations')
    dest_obj_list = []
    for dest_data in destination_data:
        dest_obj = Destinations.objects.create(location=location, **dest_data)
        dest_obj_list.append(dest_obj)
    location = TourPackages.objects.create(**validated_data)
    location.destinations.set(dest_obj_list)
    location.save()

Upvotes: 0

Related Questions