web-dev-qa-db-fra.com

Django-Rest-Framework. Mise à jour d'un objet imbriqué

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.

19
dimmg

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.

22
dimmg

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

5
djq

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()
0
Vitor Hugo Morales

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
0
FACode