Django-rest-框架3.0在嵌套序列化程序中创建或更新

使用 Django-rest-Framework 3.0和这些简单的型号:

class Book(models.Model):
title = models.CharField(max_length=50)




class Page(models.Model):
book = models.ForeignKey(Books, related_name='related_book')
text = models.CharField(max_length=500)

给出这个 JSON 请求:

{
"book_id":1,
"pages":[
{
"page_id":2,
"text":"loremipsum"
},
{
"page_id":4,
"text":"loremipsum"
}
]
}

我如何编写一个嵌套的序列化程序来处理这个 JSON,并为给定的 book的每个 page创建一个新页面或者更新它(如果存在的话)。

class RequestSerializer(serializers.Serializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)




class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page

我知道用 instance实例化序列化程序将更新当前的序列化程序,但是我应该如何在嵌套序列化程序的 create方法中使用它?

33773 次浏览

首先,您想要支持创建新的图书实例,还是只支持更新现有的图书实例?

如果你只是想创建新的图书实例,你可以这样做..。

class PageSerializer(serializers.Serializer):
text = serializers.CharField(max_length=500)


class BookSerializer(serializers.Serializer):
page = PageSerializer(many=True)
title = serializers.CharField(max_length=50)


def create(self, validated_data):
# Create the book instance
book = Book.objects.create(title=validated_data['title'])


# Create or update each page instance
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=book)
page.save()


return book

注意,这里我的 没有包含了 book_id。当我们创建 book 实例时,我们不会包含 book id。当我们更新图书实例时,我们通常会将图书 ID 作为 URL 的一部分,而不是在请求数据中。

如果您希望同时支持图书实例的创建和更新,那么您需要考虑如何处理请求中未包含但当前与图书实例关联的 页面。

您可以选择静默地忽略这些页面并保持它们的原样,也可以选择引发验证错误,或者选择删除它们。

假设您要删除请求中未包含的所有页面。

def create(self, validated_data):
# As before.
...


def update(self, instance, validated_data):
# Update the book instance
instance.title = validated_data['title']
instance.save()


# Delete any pages not included in the request
page_ids = [item['page_id'] for item in validated_data['pages']]
for page in instance.books:
if page.id not in page_ids:
page.delete()


# Create or update page instances that are in the request
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=instance)
page.save()


return instance

也有可能您希望 只有支持图书更新,而不支持创建,在这种情况下,只有包含 update()方法。

还有很多方法可以减少查询的数量,比如使用批量创建/删除,但是上面的方法可以非常简单地完成这项工作。

正如您所看到的,在处理嵌套数据时,您可能希望的行为类型有一些微妙之处,因此请仔细考虑在各种情况下您期望的行为。

还要注意,我在上面的例子中使用的是 Serializer,而不是 ModelSerializer。在这种情况下,显式地包含序列化器类中的所有字段比依赖于 ModelSerializer默认生成的自动字段集更简单。

您可以简单地使用 Drf-可写-嵌套。 它自动使您的嵌套序列化程序可写和可更新。

在你身上:

from drf_writable_nested import WritableNestedModelSerializer


class RequestSerializer(WritableNestedModelSerializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)




class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page

就是这样!

此外,如果不需要同时使用 createupdate逻辑,那么该库只支持使用其中一种逻辑。