web-dev-qa-db-ja.com

Django-Rest-Framework。ネストされたオブジェクトの更新

ネストされたオブジェクトの更新に問題があります。

だから私はこの構造に似ているモデルを持っています:

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

親から子オブジェクトを作成する必要があります。つまり、InvoiceItemsオブジェクトを作成するときに、Invoiceを直接作成します。この目的のために、次のシリアライザーを作成しました。

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

これまで、updateを除き、作成/読み取り/削除メソッドは完全に機能します。以下のロジックは正しいはずですが、何かを見逃しています。

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

どんな助けも本当に感謝されます。

25
dimmg

これは私がタスクを達成した方法です:

idフィールドにInvoiceItemSerializerフィールドを追加しました

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

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

createメソッドでも、idが渡された場合はポップします。

26
dimmg

最近、同じ問題に出会いました。私が対処した方法は、idを必須フィールドにすることでした:

class MySerializer(serializers.ModelSerializer):

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

このようにして、正しいインスタンスを取得して更新することができました

7
djq

私の場合、ネストされたオブジェクトが削除されたとしても、すべてのリストを更新したいと思います。

すべてのネストされたオブジェクトの削除で、ネストされたModel DELETEメソッドを呼び出したくありません。オブジェクト全体とネストされたオブジェクトリストを更新するだけです。

この実装の場合:1-Productには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()
1

これを試して。

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

試してみる

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