Django DRF - 如何反序列化需要外键的实例?

Django DRF - how to deserialize an instance that requires a foreign key?

我有一个页面模型和一个引用其页面的段落模型。我想通过反序列化 JSON 表示来创建这两个模块,如下所示:

{
    "page": {
        "number": 32,
        "book": "Moby Dick",
        "paragraphs": [
            {
                "label": "I am a Paragraph within the Page"
            }
        ]
    }
}

这是我的段落和页面模型:


class Paragraph(models.Model):
    page = models.ForeignKey(
        Page,
        help_text="Every Paragraph must belong to a Page",
        related_name="paragraphs",
        on_delete=models.CASCADE,
    )

    label = models.CharField(max_length=128)

    class Meta:
        db_table = 'my_paragraph'

class Page(models.Model):
    # This is not unique!
    number = models.IntegerField()
    # This is not unique!
    book = models.CharField(max_length=128)
    # but "number" and "book" together are unique.

    class Meta:
        db_table = 'my_page'

如何反序列化该表示以实例化我的模型?

我尝试在下面创建这些序列化程序:

class ParagraphSerializer (serializers.ModelSerializer):
    page = serializers.SlugRelatedField(
        slug_field="id",
        queryset=models.Page.objects.all(),
    )

    class Meta:
        model = models.Paragraph
        fields = '__all__'


class PageSerializer (serializers.ModelSerializer):
    paragraphs = ParagraphSerializer(
        many=True,
    )

    # This method never gets called, because PageSerializer validation fails.
    def create(self, validated_data):
        paragraphs_data = validated_data.pop('paragraphs')
        page = models.Page.objects.create(**validated_data)
        for paragraph_data in paragraphs_data:
            # We'll need to find some way of adding a `page` field to this paragraph...
            paragraph_data['page'] = page.id
            serializer = ParagraphSerializer(data=paragraph_data)
            serializer.is_valid()
            serializer.save()

    class Meta:
        model = models.Page
        fields = '__all__'

但是我遇到了错误,因为 SlugRelatedField 需要我的 paragraph JSON 来引用它的页面:

serializer = PageSerializer(data=page_json)
serializer.is_valid()
# is_valid() fails with the following message:
# [{'paragraphs': [{'page': [u'This field is required.']}]}, {}]

我是否应该尝试将 page 字段添加到我的 paragraphs JSON,如果是,我应该使用哪个 slug_field?不幸的是,Page 上唯一的唯一字段是 id。但是booknumber的复合键唯一的

或者有没有办法让is_valid()传递我的PageSerializer,这样我们就可以在PageSerializer.create方法中指定paragraph模型上的page字段?

我能够通过从我的 ParagraphSerializer 中删除 page 属性并在 ParagraphSerializer.create 方法中手动设置它来解决这个问题。

这可以通过将我的 Page 模型传递给序列化程序的上下文对象来实现,如下所示:

class ParagraphSerializer (serializers.ModelSerializer):

    class Meta:
        model = models.Paragraph
        exclude = 'page'

    def create(self, validated_data):
        return models.Paragraph.objects.create(
            page=self.context.get("page"),
            **validated_data
        )

class PageSerializer (serializers.ModelSerializer):
    paragraphs = ParagraphSerializer(
        many=True,
    )

    class Meta:
        model = models.Page
        fields = '__all__'

    def create(self, validated_data):
        paragraphs_data = validated_data.pop('paragraphs')
        page = models.Page.objects.create(**validated_data)
        for paragraph_data in paragraphs_data:
            # We'll need to find some way of adding a `page` field to this paragraph...
            paragraph_data['page'] = page.id
            serializer = ParagraphSerializer(
                data=paragraph_data, 
                context={"page": page}
            )
            serializer.is_valid()
            serializer.save()
        return page

我不必更改我的模型,也不必更改我的 JSON 表示!

当我看到这个博客 post 时,一切都变得有意义了,这有助于描述通过序列化程序 create 方法传递 context 对象的可能性: https://micropyramid.com/blog/django-rest-framework-send-extra-context-data-to-serializers/