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