webentwicklung-frage-antwort-db.com.de

Django-Rest-Framework. Verschachteltes Objekt aktualisieren

Ich habe ein Problem beim Aktualisieren eines verschachtelten Objekts.

Ich habe also ein Modell, dessen Struktur diesem ähnelt:

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')

Ich muss untergeordnete Objekte aus übergeordneten Objekten erstellen, und was ich damit meine, ist, InvoiceItems direkt zu erstellen, wenn ein Invoice-Objekt erstellt wird ..__ Zu diesem Zweck habe ich die folgenden Serialisierer geschrieben:

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

Bis jetzt funktionieren die Create/Read/Delete-Methoden einwandfrei, mit Ausnahme der update. Ich denke, die unten stehende Logik sollte korrekt sein, aber sie vermisst etwas.

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

Jede Hilfe wäre wirklich dankbar.

19
dimmg

So habe ich die Aufgabe erfüllt:

Ich habe ein id-Feld zur InvoiceItemSerializer hinzugefügt.

class InvoiceItemSerializer(serializers.ModelSerializer):
    ...
    id = serializers.IntegerField(required=False)
    ...

Und die Aktualisierungsmethode für die 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

Auch in der create-Methode oppe ich die id, wenn sie übergeben wird.

22
dimmg

Ich bin kürzlich auf das gleiche Problem gestoßen. Die Art und Weise, wie ich es ansprach, bestand darin, die id zu einem Pflichtfeld zu machen:

class MySerializer(serializers.ModelSerializer):

    class Meta:
        model = MyModel
        fields = ('id', 'name', 'url', )
        extra_kwargs = {'id': {'read_only': False, 'required': True}}

Auf diese Weise konnte ich die korrekte Instanz abrufen und aktualisieren

5
djq

Versuche dies.

from rest_framework.utils import model_meta

class InvoiceSerializer(serializers.ModelSerializer):
    invoice_item=InvoiceItemSerializer(many=True,required=False)

    field_map={"invoice_item" : { "model":  models.InvoiceItem
                                   "pk_field" : "id"}}    



    class Meta:
        model = models.Invoice
        fields = '__all__'

def create(self, validated_data):
    extra_data={}
    for key in self.field_map.keys():
        extra_data[key]=validated_data.pop(key,[])

    # create invoice
    invoice = models.Invoice.objects.create(**validated_data)

    for key in extra_data.keys():
        for data in extra_data[key]:
         self.field_map[key]["model"].objects.create(invoice=invoice,**data)

    return invoice
def _update(self,instance,validated_data):
    #drf default implementation
    info = model_meta.get_field_info(instance)

    for attr, value in validated_data.items():
        if attr in info.relations and info.relations[attr].to_many:
            field = getattr(instance, attr)
            field.set(value)
        else:
            setattr(instance, attr, value)
    instance.save()
    return instance

def update(self,instance,validated_data):

    extra_data={}
    for key in self.field_map.keys():
        extra_data[key]=validated_data.pop(key,[])

    instance=self._update(instance,validated_data)

    for key in extra_data.keys():
        for data in extra_data[key]:

            id=data.get(self.field_map[key]["pk_field"],None)
            if id:
                try:
                    related_instance=self.field_map[key]["model"].objects.get(id=id)
                except:
                    raise
                self._update(related_instance,data)
            else:
                self.field_map[key]["model"].objects.create(**data)

    return instance    
0
CuriousGeorge

In meinem Fall möchte ich alle Listen verschachtelter Objekte aktualisieren, auch wenn sie gelöscht werden.

Ich möchte nicht in jedem verschachtelten Objekt löschen, die geschachtelte Model DELETE-Methode aufrufen. Aktualisieren Sie einfach das gesamte Objekt und Ihre verschachtelte Objektliste.

Für diese Implementierung gilt: 1-Product hat 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

Versuchen 

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