J'ai un problème de mise à jour d'un objet imbriqué.
J'ai donc un modèle dont la structure est similaire à celle-ci:
class Invoice(models.Model):
nr = models.CharField(max_length=100)
title = models.CharField(max_length=100)
class InvoiceItem(models.Model):
name = models.CharField(max_length=100)
price = models.FloatField()
invoice = models.ForeignKey(Invoice, related_name='items')
Je dois créer des objets enfants à partir du parent, et ce que je veux dire par là est de créer InvoiceItems
directement lors de la création d'un objet Invoice
. Pour cela, j'ai écrit les sérialiseurs suivants:
class InvoiceItemSerializer(serializers.ModelSerializer):
invoice = serializers.PrimaryKeyRelatedField(queryset=Invoice.objects.all(), required=False)
class Meta:
model = InvoiceItem
class InvoiceSerializer(serializers.ModelSerializer):
items = InvoiceItemSerializer(many=True)
class Meta:
model = Invoice
def create(self, validated_data):
items = validated_data.pop('items', None)
invoice = Invoice(**validated_data)
invoice.save()
for item in items:
InvoiceItem.objects.create(invoice=invoice, **item)
return invoice
Jusqu'à présent, les méthodes create/read/delete fonctionnaient parfaitement, à l'exception de la update
. Je pense que la logique ci-dessous devrait être correcte, mais il manque quelque chose.
def update(self, instance, validated_data):
instance.nr = validated_data.get('nr', instance.nr)
instance.title = validated_data.get('title', instance.title)
instance.save()
# up till here everything is updating, however the problem appears here.
# I don't know how to get the right InvoiceItem object, because in the validated
# data I get the items queryset, but without an id.
items = validated_data.get('items')
for item in items:
inv_item = InvoiceItem.objects.get(id=?????, invoice=instance)
inv_item.name = item.get('name', inv_item.name)
inv_item.price = item.get('price', inv_item.price)
inv_item.save()
return instance
Toute aide sera grandement appréciée.
Voici comment j'ai accompli la tâche:
J'ai ajouté un champ id
à la InvoiceItemSerializer
class InvoiceItemSerializer(serializers.ModelSerializer):
...
id = serializers.IntegerField(required=False)
...
Et la méthode de mise à jour pour la InvoiceSerializer
def update(self, instance, validated_data):
instance.nr = validated_data.get('nr', instance.nr)
instance.title = validated_data.get('title', instance.title)
instance.save()
items = validated_data.get('items')
for item in items:
item_id = item.get('id', None)
if item_id:
inv_item = InvoiceItem.objects.get(id=item_id, invoice=instance)
inv_item.name = item.get('name', inv_item.name)
inv_item.price = item.get('price', inv_item.price)
inv_item.save()
else:
InvoiceItem.objects.create(account=instance, **item)
return instance
Également dans la méthode create
, je saute la id
si elle est passée.
Je suis tombé sur le même problème récemment. La façon dont je l’ai traitée consistait à forcer la id
à être un champ obligatoire:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'name', 'url', )
extra_kwargs = {'id': {'read_only': False, 'required': True}}
De cette façon, j'ai pu récupérer l'instance correcte et la mettre à jour
Dans mon cas, je souhaite mettre à jour toute la liste des objets imbriqués, même s'ils ont été supprimés.
Je ne veux pas dans chaque objet imbriqué supprimer, appelez la méthode imbriquée Model DELETE; Il suffit de mettre à jour l'objet entier et votre liste d'objets imbriqués.
Pour cette implémentation: 1-Produit a N-ProductItems
def update_product_items(self, instance, validated_data):
# get the nested objects list
product_items = validated_data.pop('products')
# get all nested objects related with this instance and make a dict(id, object)
product_items_dict = dict((i.id, i) for i in instance.products.all())
for item_data in product_items:
if 'id' in item_data:
# if exists id remove from the dict and update
product_item = product_items_dict.pop(item_data['id'])
product_item.quantity = item_data['quantity']
product_item.size_pmg = item_data['size_pmg']
product_item.size_number = item_data['size_number']
product_item.color = item_data['color']
product_item.save()
else:
# else create a new object
ProductItem.objects.create(product=instance, **item_data)
# delete remaining elements because they're not present in my update call
if len(product_items_dict) > 0:
for item in product_items_dict.values():
item.delete()
Essayer
def update(self, instance, validated_data):
instance.nr = validated_data.get('nr', instance.nr)
instance.title = validated_data.get('title', instance.title)
instance.save()
items = validated_data.get('items')
for item in items:
inv_item = InvoiceItem.objects.get(invoice=instance, pk=item.pk)
inv_item.name = item.get('name', inv_item.name)
inv_item.price = item.get('price', inv_item.price)
inv_item.invoice = instance
inv_item.save()
instance.save()
return instance