I have a simple django application with the following models:
class Product(models.Model):
__metaclass__ = ABCMeta
title = models.CharField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=100, unique=True)
price = models.IntegerField()
is_published = models.BooleanField(default=True)
@abstractmethod
def __str__(self):
pass
@abstractmethod
def get_absolute_url(self):
pass
class SupplyType(models.Model):
title = models.CharField(max_length=10)
slug = models.SlugField(max_length=100, unique=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('supply_type_detail', kwargs={'slug': self.slug})
class Processor(Product):
supply_type = models.ForeignKey(SupplyType, on_delete=models.CASCADE)
cores_amount = models.IntegerField()
threads_amount = models.IntegerField()
technological_process = models.IntegerField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('processor_detail', kwargs={'slug': self.slug})
The corresponding serializers were written for them:
class SupplyTypeSerializer(ModelSerializer):
class Meta:
model = SupplyType
fields = '__all__'
class ProcessorSerializer(ModelSerializer):
class Meta:
model = Processor
fields = '__all__'
depth = 1
The corresponding views were also written (I will give only the views of creation for an example):
class ProcessorCreateAPIView(CreateAPIView):
model = Processor
serializer_class = ProcessorSerializer
class SupplyTypeCreateAPIView(CreateAPIView):
model = SupplyType
serializer_class = SupplyTypeSerializer
When I try to add "Supply Type" using POST request, it works successfully.
However, when I try to add a processor like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": 1
}
I get an error:
django.db.utils.IntegrityError: null value in column "supply_type_id" of relation "store_processor" violates not-null constraint DETAIL: Failing row contains (1, 2, 4, 14, null).
Ultimately, there are the following questions: how to fix this and how, in this case, to add a processor with the desired supply type through the API (still through the id) or in some other way?
As a result, when I do a GET request, I would like to get something like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type":
{
"id": 1,
"title": "OEM",
"slug": "oem"
}
And yeah, sorry for my english.
CodePudding user response:
You need to use the concept of nested serializers. See below code
class ProcessorSerializer(ModelSerializer):
supply_type = SupplyTypeSerializer()
class Meta:
model = Processor
fields = '__all__'
As a result, when you do a GET request, you would get something like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": {
"id": 1,
"title": "OEM",
"slug": "oem"
}
}
In order to create a Processor you will have to pass the supply_type dict object similar to what you get in the output. But since you want to pass the supply_type id instead, you could override the to_internal_value method as follows and set supply_type field as read_only:
def to_internal_value(self, data):
supply_type_id = data.get('supply_type')
internal_data = super().to_internal_value(data)
try:
supply_type = SupplyType.objects.get(id=supply_type_id)
except SupplyType.DoesNotExist:
raise serializers.ValidationError(
{'supply_type': ['Item does not exist']},
)
internal_data['supply_type'] = supply_type
return internal_data
Now you can create a Processor like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": 1
}
The final code:
class ProcessorSerializer(serializers.ModelSerializer):
supply_type = SupplyTypeSerializer(read_only=True)
class Meta:
model = Processor
fields = '__all__'
def to_internal_value(self, data):
supply_type_id = data.get('supply_type')
internal_data = super().to_internal_value(data)
try:
supply_type = SupplyType.objects.get(id=supply_type_id)
except SupplyType.DoesNotExist:
raise serializers.ValidationError(
{'supply_type': ['Item does not exist']},
)
internal_data['supply_type'] = supply_type
return internal_data