Django Rest Framework:如何将数据传递给嵌套的序列化程序并仅在自定义验证后创建对象 [英] Django Rest Framework: How to pass data to a nested Serializer and create an object only after custom validation
问题描述
我有两个模型:
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()
和序列化器
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
PageSerializer
中有一个 validate_text
方法.create
方法永远不会调用 PageSerializer
并且 page_data
永远不会被验证.
There is a validate_text
method in PageSerializer
. The create
method will never call the PageSerializer
and the page_data
is never validated.
所以我尝试了另一种方法:
So I tried another approach as:
@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"
}
]
}
但是上面的方法抛出错误:
But the above approach throws error:
{
"link": [
"Incorrect type. Expected pk value, received Link."
]
}
我还找到了导致此错误的原因:虽然我使用 pk
值 1
的 Link
发布数据,但数据在传递时从 BookSerializer
到 PageSerializer
出现如下:{link": /go_to_link/", page_no":52,文本":sometext"}
I also found why this error is caused: Though I am posting data with pk
value 1
of a Link
, the data when passed to the PageSerializer
from the BookSerializer
appears as such: {"link": "/go_to_link/", "page_no":52, "text": "sometext"}
为什么将Link
的实例传递给PageSerializer
而我发送的是Link
的pk
?
Why is an instance of Link
passed to the PageSerializer
whereas what I sent is pk
of Link
?
推荐答案
使用嵌套序列化程序验证嵌套对象:
To validate a nested object using a nested serializer:
@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"}]
}
在上面的数据中,我们发送了 Link
对象的 id
.但是在上面定义的BookSerializer
的create
方法中,我们发送的数据发生了变化:
In the above data we are sending an id
of the Link
object. But in the create
method of the BookSerializer
defined above, the data we sent changes to:
{
"title": "Book Title",
"pages": [{
"link":Link Object (2),#<= changed to the Link object with id 2
"page_no":52,
"text":"sometext"}]
}
而 PageSerializer
实际上是为了接收 link
的 pk
值,即 "link": 2
而不是 "link":Link Object (2)
.因此它抛出错误:
And the PageSerializer
is actually meant to receive an pk
value of the link
i.e, "link": 2
instead of "link":Link Object (2)
. Hence it throws error:
<代码>{关联": [类型不正确.预期的 pk 值,收到链接."]}
所以解决方法是覆盖嵌套序列化器的to_internal_value
方法,将接收到的Link Object (2)
对象转换为其pk
价值.
So the workaround is to override the to_internal_value
method of the nested serializer to convert the received Link Object (2)
object to its pk
value.
所以你的 PageSerializer
类应该是:
So your PageSerializer
class should then be:
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 方法中编写验证并违反 DRY.
That should allow the nested serializer to do the validation instead of writing validation inside the create method of the parent serializer and violating DRY.
这篇关于Django Rest Framework:如何将数据传递给嵌套的序列化程序并仅在自定义验证后创建对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!