iamafasha
iamafasha

Reputation: 794

How do I do a post request with related serializers Django rest framework

I have model Package with Supplier and PackageSize, foreign keys and a field to which is also a foreign key of a Shipping model which contains where the supplier wants to ship the package so to make sure I a user can submit the whole information in one request, I created my serializers and linked them like such.

serializers.py

from users.models import Supplier
from packages.models import Package , PackageSize , ShippingLocation , Shipping

    class ShippingLocationSerializer(serializers.ModelSerializer):
        class Meta:
            model= ShippingLocation
            fields = ['latitude','longitude']
        def create(self, validated_data):
            return ShippingLocation(**validated_data)

    class PackageSizeSerializer(serializers.ModelSerializer):
        class Meta:
            model= PackageSize
            fields = ['length', 'width' ,'height' ,'weight']
        def create(self, validated_data):
            """
            docstring
            """
            pass

    class ShippingSerializer(serializers.ModelSerializer):
        location = ShippingLocationSerializer(source='location_set')
        class Meta:
            model = Shipping
            fields = [
                'first_name', 
                'last_name',
                'phone_number',
                'email',
                'street_address',
                'village',
                'district',
                'country',
                'location'
                ]
        def create(self, validated_data):
            """
            docstring
            """
            pass

    class SupplierPackageSerializer(serializers.ModelSerializer):
        size = PackageSizeSerializer(source='size_set')
        shipping_location= ShippingSerializer(source='to_set')
        class Meta:
            model = Package
            fields = ['supplier', 'name', 'size', 'shipping_location', ]
            read_only_fields = ['supplier']
        
        def create(self, validated_data):
            user =Supplier.objects.get(username=self.context['request'].user)
            return Package(supplier=user, **validated_data )

and created my views like such

view.py

from rest_framework import generics
from packages.models import Package
from .serializers import , SupplierPackageSerializer
from rest_framework.permissions import IsAuthenticated

class SupplierPackageViewSet(generics.ListCreateAPIView):
    serializer_class = SupplierPackageSerializer
    queryset = Package.objects.all().order_by('-name')
    permission_classes = [IsAuthenticated]
    

models.py

from django.db import models    
from django.conf import settings
from users.models import Supplier

class ShippingLocation(models.Model):
    latitude = models.IntegerField()
    longitude = models.IntegerField()

class Shipping (models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    phone_number =  models.CharField(max_length=14)
    email = models.EmailField()
    street_address = models.CharField(max_length=30)
    village = models.CharField(max_length=30)
    district = models.CharField(max_length=30)
    country = models.CharField(max_length=30)
    location = models.OneToOneField(ShippingLocation , default=None, on_delete=models.CASCADE )

    # Returns the string representation of the model.
    def __str__(self):
        return self.email

class PackageSize(models.Model):
    length = models.IntegerField()
    width = models.IntegerField()
    height = models.IntegerField()
    weight = models.IntegerField()

# Create your models here.
class Package(models.Model):
    TYPE = (
        ('1', 'Envelope'),
        ('2', 'Parcel'),
        ('2', 'Soft'),
        ('2', 'Freezed'),
    )
    TYPE = (
        ('1', 'CREATED IN SYSTEM'),
        ('2', ''),
        ('2', 'Soft'),
        ('2', 'Freezed'),
    )
    name = models.CharField(max_length=30)
    supplier = models.ForeignKey( Supplier , on_delete=models.DO_NOTHING)
    to =  models.ForeignKey(Shipping, default=None, on_delete=models.CASCADE )
    size = models.ForeignKey( PackageSize, default=None, on_delete=models.CASCADE )
    type = models.CharField(max_length=1, default="1", choices=TYPE)
    
    def __str__(self):
        return self.name

the challenge is when I submit the data it is validated very well but it cannot be saved and I get this error

TypeError at /supplier/package
Package() got an unexpected keyword argument 'size_set'

Upvotes: 0

Views: 1386

Answers (2)

araldhafeeri
araldhafeeri

Reputation: 187

EDIT!

serializers.py

class StringSerializer(serializers.StringRelatedField):
    def to_internal_value(self, value):
        return value

class SupplierPackageSerializer(serializers.ModelSerializer):
        item1 = StringSerializer()
        item2 = StringSerializer()
        size = serializers.SerializerMethodField()
        shipping_location= serializers.SerializerMethodField()
        class Meta:
            model = Package
            fields = ['supplier', 'name', 'size', 'shipping_location', ]
            read_only_fields = ['supplier']
        # add this  
        def get_size(self, obj):
            return PackageSizeSerializer(obj.item1).data
        
        def get_shipping_location(self,obj):
            return ShippingSerializer(obj.item2).data

DOCS:

https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield

https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield

So basically The StringSerializer() class is a way to return a string representation of the model data.

While SerializerMethodField This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.

Upvotes: 0

iklinac
iklinac

Reputation: 15738

Model Package has foreign key to PackageSize ( only one size per package) so source is not size_set but just size

    size = PackageSizeSerializer()

EDIT:

You will also have to override create method on serializer to save related object as documented in writable nested serializer

Something in a line of

def create(self, validated_data):
        size_data= validated_data.pop('size', None)
        if size_data:
            package_size= PackageSize.objects.get_or_create(**size_data)[0]
            validated_data['size'] = package_size
        return Package.objects.create(**validated_data)

Upvotes: 1

Related Questions