[英]Django-Rest-Framework. Updating nested object
我在更新嵌套對象時遇到問題。
所以我有一個結構類似於這個的模型:
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')
我需要從父對象創建子對象,我的意思是在創建Invoice
對象時直接創建InvoiceItems
。 為此,我編寫了以下序列化程序:
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
到目前為止, create/read/delete 方法運行良好,除了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
任何幫助將非常感激。
這是我完成任務的方式:
我在InvoiceItemSerializer
中添加了一個id
字段
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
。
所有這些解決方案對我來說似乎太復雜或太具體,我最終使用了教程中的代碼,這些代碼非常簡單且可重用:
from rest_framework import serializers
from django.contrib.auth import get_user_model
from myapp.models import UserProfile
# You should already have this somewhere
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['nested', 'fields', 'you', 'can', 'edit']
class UserSerializer(serializers.ModelSerializer):
# CHANGE "userprofile" here to match your one-to-one field name
userprofile = UserProfileSerializer()
def update(self, instance, validated_data):
# CHANGE "userprofile" here to match your one-to-one field name
if 'userprofile' in validated_data:
nested_serializer = self.fields['userprofile']
nested_instance = instance.userprofile
nested_data = validated_data.pop('userprofile')
# Runs the update on whatever serializer the nested data belongs to
nested_serializer.update(nested_instance, nested_data)
# Runs the original parent update(), since the nested fields were
# "popped" out of the data
return super(UserSerializer, self).update(instance, validated_data)
編輯:修正,我在嘗試更新嵌套字段之前添加了一個檢查嵌套字段的存在。
我最近遇到了同樣的問題。 我解決它的方式是強制id
成為必填字段:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'name', 'url', )
extra_kwargs = {'id': {'read_only': False, 'required': True}}
這樣我就能夠檢索正確的實例並更新它
我認為 Vitor Hugo Morales 的回答很棒,並且希望通過循環鍵將對象中的每個字段分配給經過驗證的數據中的字段,而不是像他所做的那樣對其進行硬編碼,從而貢獻我的一分錢。 例如,
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'])
# remove id from validated data as we don't require it.
item_data.pop('id')
# loop through the rest of keys in validated data to assign it to its respective field
for key in item_data.keys():
setattr(product_item,key,item_data[key])
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()
就我而言,我希望更新所有嵌套對象列表,即使它們被刪除。
我不想在每一個嵌套對象中刪除,調用嵌套模型的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()
我會添加事務並使用一些 util django 方法
from django.db import transaction
def update(self, instance, validated_data):
with transaction.atomic():
items = validated_data.pop('items', None)
for key, value in validated_data.items():
setattr(instance, key, value)
instance.save()
for item in items:
inv_item, created = InvoiceItem.objects.update_or_create(id=item['id'], invoice=instance, defaults={**item})
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
嘗試這個。
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
drf-writable-nested 包提供了可寫的嵌套模型序列化器,它允許使用嵌套的相關數據創建/更新模型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.