首页 文章

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

提问于
浏览
67

使用 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 方法中使用它?

2 回答

  • 92

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

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

    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 . 当我们're creating book instances we won' t包括书籍ID . 当我们're updating book instances we'll通常将book 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 默认生成的自动字段集 .

  • 0

    你可以简单地使用drf-writable-nested . 它会自动使嵌套的序列化程序可写和可更新 .

    在你 serializers.py

    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 逻辑中的一个 .

相关问题