Почему мое поле «идентификатор» исчезает в моем сериализаторе (множество)?

#django #django-rest-framework

Вопрос:

У меня есть объект «Цена», который может содержать несколько объектов «Цена» с множеством отношений.

Я использую платформу django rest для публикации api, и я хотел бы, чтобы при использовании PUT или PATCH в таблице цен я также мог изменять содержимое PriceLine. Цель состоит в том, чтобы иметь уникальный метод ОБНОВЛЕНИЯ, позволяющий изменять экземпляры 2 объектов.

Мои модели:

 class PriceTable(models.Model):

    name = models.CharField(_('Name'), max_length=255)
    client = models.ForeignKey(
        "client.Client",
        verbose_name=_('client'),
        related_name="price_table_client",
        on_delete=models.CASCADE
    )
    lines = models.ManyToManyField(
        'price.PriceLine',
        verbose_name=_('lines'),
        related_name="price_table_lines",
        blank=True,
    )
    standard = models.BooleanField(_('Standard'), default=False)

    class Meta:
        verbose_name = _("price table")
        verbose_name_plural = _("price tables")

    def __str__(self):
        return self.name

class PriceLine(models.Model):

    price = models.FloatField(_('Price'))
    product = models.ForeignKey(
        "client.Product",
        verbose_name=_('product'),
        related_name="price_line_product",
        on_delete=models.CASCADE,
    )

    class Meta:
        verbose_name = _("price line")
        verbose_name_plural = _("price line")

    def __str__(self):
        return f"{self.product.name} : {self.price} €"
 

Я хочу иметь возможность отправлять JSON этого формата для изменения как таблицы, так и ее строк:

 {
    "id": 16,
    "lines": [
        {
            "id": 1,
            "price": 20.0,
            "product": 1
        },
        {
            "id": 2,
            "price": 45.0,
            "product": 2
        }
    ],
    "name": "test"
}
 

Для этого я пытаюсь переопределить метод обновления моего сериализатора:

 class PriceTableSerializer(serializers.ModelSerializer):
    """
    PriceTableSerializer
    """
    lines = PriceLineSerializerTest(many=True)
    class Meta:
        model = PriceTable
        exclude = ['standard', 'client',]
        
    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        lines = validated_data.get('lines')
        
        print(lines)

        # not python code
        # for target_line in lines:
        # if instance.id == target_line.id
        # instance.price = target_line.price
        # ...

        return instance
 

5 прокомментированных строк-это логика, которую я хотел бы реализовать.
Я хочу просмотреть полученный массив строк, и если идентификатор этой строки равен идентификатору строки в моем экземпляре, я изменяю значения этой строки.

Проблема в том, что идентификатор исчезает. Когда я печатаю lines переменную, я получаю это:

 [OrderedDict([('price', 20.0), ('product', <Product: Test import2>)])]
OrderedDict([('price', 20.0), ('product', <Product: Test import2>)])
 

Что случилось с удостоверением личности?

Комментарии:

1. Он очищается в Product объект.

2. Я не понимаю. У меня проблема не в исчезновении идентификатора продукта, а в исчезновении идентификатора объекта PriceLine. (строки полей json)

3. Это поле в PriceLineSerializerTest сериализаторе?

4. да, fields = ('id', 'price', 'product',) в PriceLineSerializerTest

5. Если я поставлю id = serializers.IntegerField() , это сработает, но это безумие ….

Ответ №1:

Как говорится в документах в https://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-update:

Вам нужно будет добавить явное поле идентификатора в сериализатор экземпляра. Неявно сгенерированное поле идентификатора по умолчанию помечено как только для чтения. Это приводит к его удалению при обновлении. Как только вы объявите его явно, он будет доступен в методе обновления сериализатора списков.

Поэтому вы должны заявить id о себе за то, что можете использовать это:

 class PriceLineSerializerTest(serializers.ModelSerializer):
    id = serializers.IntegerField()

    class Meta:
        model = PriceLine
        exclude = ['id', ...]