Проблема со связанными объектами в REST Framework

У меня есть простое приложение django со следующими моделями:

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})

Для них были написаны соответствующие сериализаторы:

class SupplyTypeSerializer(ModelSerializer):
    class Meta:
        model = SupplyType
        fields = '__all__'


class ProcessorSerializer(ModelSerializer):
    class Meta:
        model = Processor
        fields = '__all__'
        depth = 1

Также были написаны соответствующие представления (для примера я приведу только представления о творении):

class ProcessorCreateAPIView(CreateAPIView):
    model = Processor
    serializer_class = ProcessorSerializer


class SupplyTypeCreateAPIView(CreateAPIView):
    model = SupplyType
    serializer_class = SupplyTypeSerializer

Когда я пытаюсь добавить "Тип поставки" с помощью POST запроса, он работает успешно.

Однако, когда я пытаюсь добавить процессор следующим образом:

{
    "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
}

Я получаю ошибку:

django.db.utils.IntegrityError: null значение в столбце "supply_type_id" отношения "store_processor" нарушает not-null ограничение ПОДРОБНОСТИ: Ошибочная строка содержит (1, 2, 4, 14, null).

В итоге возникают следующие вопросы: как это исправить и как, в таком случае, добавить процессор с нужным типом поставки через API (все равно через id) или каким-то другим способом?

В результате, когда я делаю GET запрос, я хотел бы получить что-то вроде этого:

{
    "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.

Вам необходимо использовать концепцию вложенных сериализаторов. Смотрите следующий код

class ProcessorSerializer(ModelSerializer):
    supply_type = SupplyTypeSerializer()

    class Meta:
        model = Processor
        fields = '__all__'

В результате, когда вы выполняете GET-запрос, вы получаете что-то вроде этого:

{
    "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"
    }
}

Для создания процессора вам нужно передать объект supply_type dict, подобный тому, что вы получите в выводе. Но поскольку вы хотите передать вместо этого идентификатор supply_type, вы можете переопределить метод to_internal_value следующим образом и установить поле supply_type как 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

Теперь вы можете создать процессор следующим образом:

{
    "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
}

Окончательный код:

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
Вернуться на верх