Reputation: 794
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
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
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