darkvodka
darkvodka

Reputation: 333

Why does my "id" field disappear in my serializer (manytomany)?

I have a "PriceTable" object that can contain several "PriceLine" objects with a manytomany relationship.

I use django rest framework to publish an api and I would like that when I use PUT or PATCH on PriceTable, I can also modify the content of PriceLine. The goal is to have a unique UPDATE method to be able to modify the instances of the 2 objects.

My models:

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

I want to be able to send a JSON of this format to modify both the table and its lines:

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

For this, I try to override the update method of my serializer:

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

The 5 commented lines, are the logic I would like to implement. I want to browse the received array of rows and if the id of this row is equal to the id of a row in my instance, I change the values of this row.

The problem is that the id disappears. When I print the lines variable, I get this:

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

What happened to the id?

Upvotes: 4

Views: 1996

Answers (1)

Amin
Amin

Reputation: 2874

As docs says in https://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-update:

You will need to add an explicit id field to the instance serializer. The default implicitly-generated id field is marked as read_only. This causes it to be removed on updates. Once you declare it explicitly, it will be available in the list serializer's update method.

So you should declare id yourself for being able to use that:

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

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

Upvotes: 6

Related Questions