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
どんな助けも本当に感謝されます。
これは私がタスクを達成した方法です:
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
が渡された場合はポップします。
最近、同じ問題に出会いました。私が対処した方法は、id
を必須フィールドにすることでした:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'name', 'url', )
extra_kwargs = {'id': {'read_only': False, 'required': True}}
このようにして、正しいインスタンスを取得して更新することができました
私の場合、ネストされたオブジェクトが削除されたとしても、すべてのリストを更新したいと思います。
すべてのネストされたオブジェクトの削除で、ネストされた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()
これを試して。
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
試してみる
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