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()
和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
PageSerializer
中有一个validate_text
方法。 create
方法永远不会调用 PageSerializer
并且 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."
]
}
我还发现了导致此错误的原因:虽然我发布的数据具有 Link
的 pk
值 1
,但传递给 PageSerializer
时的数据来自 BookSerializer
显示为:{"link": "/go_to_link/", "page_no":52, "text": "sometext"}
为什么 Link
的实例传递给 PageSerializer
而我发送的是 Link
的 pk
?
当您调用 serializer.is_valid(raise_exception=True/False)
时,它会自动调用嵌套序列化程序的验证函数。当您调用 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
使用嵌套序列化程序验证嵌套对象:
@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
方法中,我们发送的数据变为:
{
"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)
。因此它抛出错误:
{
"link": [
"Incorrect type. Expected pk value, received Link."
]
}
因此解决方法是重写嵌套序列化程序的 to_internal_value
方法,将接收到的 Link Object (2)
对象转换为其 pk
值。
所以你的 PageSerializer
class 应该是:
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
这应该允许嵌套序列化程序执行验证,而不是在父序列化程序的创建方法中编写验证并违反 DRY。
我有两个模型:
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
PageSerializer
中有一个validate_text
方法。 create
方法永远不会调用 PageSerializer
并且 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."
]
}
我还发现了导致此错误的原因:虽然我发布的数据具有 Link
的 pk
值 1
,但传递给 PageSerializer
时的数据来自 BookSerializer
显示为:{"link": "/go_to_link/", "page_no":52, "text": "sometext"}
为什么 Link
的实例传递给 PageSerializer
而我发送的是 Link
的 pk
?
当您调用 serializer.is_valid(raise_exception=True/False)
时,它会自动调用嵌套序列化程序的验证函数。当您调用 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
使用嵌套序列化程序验证嵌套对象:
@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
方法中,我们发送的数据变为:
{
"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)
。因此它抛出错误:
{
"link": [
"Incorrect type. Expected pk value, received Link."
]
}
因此解决方法是重写嵌套序列化程序的 to_internal_value
方法,将接收到的 Link Object (2)
对象转换为其 pk
值。
所以你的 PageSerializer
class 应该是:
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
这应该允许嵌套序列化程序执行验证,而不是在父序列化程序的创建方法中编写验证并违反 DRY。