With django-rest-framework 3.0 and having these simple models:

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)

And given this JSON request:

{
   "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

I know that instantiating the serializer with an instance will update the current one but how should I use it inside the create method of nested serializer?

推荐答案

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

If you only ever wanted to create new book instances you could do something like this...

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

Note that I haven't included the book_id here. When we're creating book instances we won't be including a book id. When we're updating book instances we'll typically include the book id as part of the URL, rather than in the request data.

If you want to support both create and update of book instances then you need to think about how you want to handle pages that are not included in the request, but are currently associated with the book instance.

You might choose to silently ignore those pages and leave them as they are, you might want to raise a validation error, or you might want to delete them.

Let's assume that you want to delete any pages not included in the request.

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

It's also possible that you might want to only support book updates, and not support creation, in which case, only include the update() method.

也有各种方法可以减少查询的数量.使用批量创建/删除,但上述操作将以相当简单的方式完成工作.

As you can see there are subtleties in the types of behavior you might want when dealing with nested data, so think carefully about exactly what behavior you're expecting in various cases.

还要注意,我在上面的例子中使用了Serializer,而不是ModelSerializer.在这种情况下,只需显式地包含serializer类中的所有字段,而不是依赖ModelSerializer默认生成的自动字段集,就更简单了.

Json相关问答推荐

使用单元和非单元版本反序列化Rust中的枚举,而无需编写自定义反序列化程序

与错误相关的未定义&Quot;不是有效的JSON

从Postgres表中的JSON中提取值

如何使用PowerShell从ExchangeOnline命令执行中获得JSON输出

当由.sh脚本执行时,AWS查询字符串不会提取任何数据

jq 对特定键进行过滤并将值整理到单个 csv 单元格中

当列为空时从 SQL 获取 JSON

XSLT 3.0 Json-to-xml,json 包含 html struct

(已回答)JSON 读取函数返回未定义而不是预期值 - Typescript

使用 jq 从字符串列表开始创建对象

正向闪烁后的微调值

jq EOF 处的无效数字文字将 json 值更新为 0.0.x

SyntaxError:Object.parse(本机)AngularJS中的意外标记o

Jackson Json:如何将数组转换为 JsonNode 和 ObjectNode?

Json.Net:用于自定义命名的 JsonSerializer-Attribute

Spring MVC:不反序列化 JSON 请求正文

可以在 SharedPreferences 中保存 JSON 数组吗?

JSON 格式的 Amazon S3 响应?

如何使用 Jackson 的 objectMapper 反序列化接口字段?

Laravel 5 控制器将 JSON 整数作为字符串发送