django-rest-framework 3.0在嵌套的序列化程序中创建或更新

nor*_*tpy 71 python django serialization json django-rest-framework

使用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)
Run Code Online (Sandbox Code Playgroud)

并且给出了这个JSON请求:

{
   "book_id":1,
   "pages":[
      {
         "page_id":2,
         "text":"loremipsum"
      },
      {
         "page_id":4,
         "text":"loremipsum"
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

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

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


class PageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Page
Run Code Online (Sandbox Code Playgroud)

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

Tom*_*tie 97

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

如果你只想创建新的书籍实例,你可以做这样的事情......

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
Run Code Online (Sandbox Code Playgroud)

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

如果你想支持创建和书籍实例的更新,那么你需要考虑一下你要如何处理未包含在请求页面,但当前与书实例相关联.

您可以选择静默忽略这些页面并保持原样,您可能希望引发验证错误,或者您可能希望删除它们.

假设您要删除请求中未包含的任何页面.

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
Run Code Online (Sandbox Code Playgroud)

您可能还希望支持书籍更新,而不支持创建,在这种情况下,包括update()方法.

还有各种方法可以减少查询次数,例如.使用批量创建/删除,但上面会以相当简单的方式完成工作.

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

另请注意,我一直在使用Serializer上面的示例而不是ModelSerializer.在这种情况下,仅显式地包含序列化程序类中的所有字段更简单,而不是依赖于ModelSerializer默认生成的自动字段集.

  • @TomChristie你能帮我一个忙吗?看看我的[尝试创建嵌套资源](http://stackoverflow.com/questions/29126707/improperly-configured-nested-resource-using-hyperlinkedmodelserializer)?在尝试*[django-rest-framework-nested-resource](https://github.com/simpleenergy/django-rest-framework-nested-resource)*,*[drf-extensions](https ://github.com/chibisov/drf-extensions)*和*[drf-nested-routers](https://github.com/alanjds/drf-nested-routers)* - 都没有成功.我很高兴切换到实际工作的东西. (3认同)
  • 为什么覆盖是在序列化器中而不是在视图中,例如:http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing? (2认同)
  • @TomChristie如果我使用ModelSerializer而不是Serializer,它会过滤掉`page_id`. (2认同)