dim*_*mmg 27 python django rest 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')
Run Code Online (Sandbox Code Playgroud)
我需要从父级创建子对象,我的意思是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
Run Code Online (Sandbox Code Playgroud)
到目前为止,创建/读取/删除方法完美地工作,除了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
Run Code Online (Sandbox Code Playgroud)
任何帮助将非常感激.
dim*_*mmg 29
这是我完成任务的方式:
我已经添加了一个id
字段InvoiceItemSerializer
class InvoiceItemSerializer(serializers.ModelSerializer):
...
id = serializers.IntegerField(required=False)
...
Run Code Online (Sandbox Code Playgroud)
和更新方法 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
Run Code Online (Sandbox Code Playgroud)
同样在create
方法中我弹出id
如果它被传递.
我认为 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()
Run Code Online (Sandbox Code Playgroud)
我最近遇到了同样的问题.我解决它的方式是强制id
成为必填字段:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'name', 'url', )
extra_kwargs = {'id': {'read_only': False, 'required': True}}
Run Code Online (Sandbox Code Playgroud)
这样我就能够检索正确的实例并进行更新
所有这些解决方案似乎过于复杂或者太具体的我,我结束了使用代码从这里教程这是难以置信的简单,可重复使用:
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)
Run Code Online (Sandbox Code Playgroud)
编辑:修正,我在尝试更新它之前添加了对嵌套字段是否存在的检查。
归档时间: |
|
查看次数: |
13959 次 |
最近记录: |