代码之家  ›  专栏  ›  技术社区  ›  Sreekanth Reddy Balne

Django Rest框架:如何将数据传递给嵌套序列化程序并仅在自定义验证之后创建对象

  •  1
  • Sreekanth Reddy Balne  · 技术社区  · 6 年前

    我有两个模型:

    class Book(AppModel):
        title = models.CharField(max_length=255)
    
    class Link(AppModel):
        link = models.CharField(max_length=255)
    
    class Page(AppModel):
        book= models.ForeignKey("Book",related_name="pages",on_delete=models.CASCADE)
        link = models.ForeignKey("Link", related_name="pages", on_delete=models.CASCADE)
        page_no = models.IntegerField()
        text = models.TextField()
    

    serializers

    class LinkSerializer(serializers.ModelSerializer):
        class Meta:
           model = Link
           fields = ['link']
    
    class PageSerializer(serializers.ModelSerializer):
        class Meta:
            model = Page
            fields = ('link','text','page_no')
    
        def validate_text(self, value):
            #some validation is done here.
    
        def validate_link(self, value):
            #some validation is done here.
    
    class BookSerializer(serializers.ModelSerializer):
        pages = PageSerializer(many=True)
        class Meta:
            model = Book
            fields = ('title','pages')
    
        @transaction.atomic
        def create(self, validated_data):
            pages_data= validated_data.pop('pages')
            book = self.Meta.model.objects.create(**validated_data)
            for page_data in pages_data:
                Page.objects.create(book=book, **page_data)
            return book
    

    有一个 validate_text PageSerializer . 这个 create 方法永远不会调用 页面序列化程序 以及 page_data

    所以我尝试了另一种方法:

    @transaction.atomic
    def create(self, validated_data):
        pages_data = validated_data.pop('pages')
        book= self.Meta.model.objects.create(**validated_data)
        for page_data in pages_data:
            page = Page(book=book)
            page_serializer = PageSerializer(page, data = page_data)
            if page_serializer.is_valid():
                page_serializer.save()
            else:
                raise serializers.ValidationError(page_serializer.errors)
        return book
    

    {
    "title": "Book Title",
    "pages": [{
    "link":1,"page_no":52, "text":"sometext"}]
    }
    

    但上述方法会产生错误:

    {
    "link": [
        "Incorrect type. Expected pk value, received Link."
        ]
    }
    

    pk 价值 1 Link ,将数据传递到 BookSerializer 如下所示: {"link": "/go_to_link/", "page_no":52, "text": "sometext"}

    链接 传递给 页面序列化程序 而我送的是 主键 属于 ? 这里需要帮助。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Tolqinbek Isoqov    6 年前

    当你打电话的时候 serializer.is_valid(raise_exception=True/False) 它自动调用嵌套序列化程序的validate函数。当你打电话的时候 serializer.save(**kwargs) 序列化程序将经过验证的数据传递到 create(self, validated_data) update(self, instance, validated_data) 序列化程序的函数。此外,在经过验证的数据中,ForeignKey字段返回了一个对象。

    def create(self, validated_data):
        pages_data = validated_data.pop('pages') # [{'link': Linkobject, ...}]
        book= self.Meta.model.objects.create(**validated_data)
        for page_data in pages_data:
            page = Page(book=book)
            page_serializer = PageSerializer(page, data = page_data) # here you are sending object to validation again
            if page_serializer.is_valid():
                page_serializer.save()
            else:
                raise serializers.ValidationError(page_serializer.errors)
        return book
    
        2
  •  1
  •   Sreekanth Reddy Balne    6 年前

    要使用嵌套序列化程序验证嵌套对象,请执行以下操作:

    @transaction.atomic
    def create(self, validated_data):
        pages_data = validated_data.pop('pages') #pages data of a book
        book= self.Meta.model.objects.create(**validated_data)
        for page_data in pages_data:
            page = Page(book=book)
            page_serializer = PageSerializer(page, data = page_data)
            if page_serializer.is_valid(): #PageSerializer does the validation
                page_serializer.save()
            else:
                raise serializers.ValidationError(page_serializer.errors) #throws errors if any
        return book
    

    假设您将数据发送为:

    {
        "title": "Book Title",
        "pages": [{
            "link":2,#<= this one here
            "page_no":52, 
            "text":"sometext"}]
    }
    

    在上述数据中,我们发送了一个 id Link 对象。但是在 create 方法 BookSerializer 我们将更改发送到的数据定义如下:

    {
        "title": "Book Title",
        "pages": [{
            "link":Link Object (2),#<= changed to the Link object with id 2
            "page_no":52, 
            "text":"sometext"}]
    }
    

    以及 PageSerializer 实际上是要接受 pk 价值 link 即, "link": 2 而不是 "link":Link Object (2)

    { "link": [ "Incorrect type. Expected pk value, received Link." ] }

    所以解决方法是重写 to_internal_value 方法来转换接收到的 Link Object (2) 反对其 主键 价值观。

    所以你的 页面序列化程序

    class PageSerializer(serializers.ModelSerializer):
        class Meta:
            model = Page
            fields = ('link','text','page_no')
    
        def to_internal_value(self, data): 
            link_data = data.get("link")
            if isinstance(link_data, Link): #if object is received
                data["link"] = link_data.pk # change to its pk value
            obj = super(PageSerializer, self).to_internal_value(data)
            return obj
    
        def validate_text(self, value):
            #some validation is done here.
    
        def validate_link(self, value):
            #some validation is done here.
    

    以及父序列化程序:

    class BookSerializer(serializers.ModelSerializer):
        pages = PageSerializer(many=True)
        class Meta:
            model = Book
            fields = ('title','pages')
    
        @transaction.atomic
        def create(self, validated_data):
            pages_data = validated_data.pop('pages')#pages data of a book
            book= self.Meta.model.objects.create(**validated_data)
            for page_data in pages_data:
                page = Page(book=book)
                page_serializer = PageSerializer(page, data = page_data)
                if page_serializer.is_valid(): #PageSerializer does the validation
                    page_serializer.save()
                else:
                    raise serializers.ValidationError(page_serializer.errors) #throws errors if any
            return book
    

    这应该允许嵌套的序列化程序执行验证,而不是在父序列化程序的create方法中写入验证,并进行验证。