Django Rest Framework——我可以在序列化程序的创建方法中允许 pk id 或完整对象吗?



假设我有以下模型:

class Author(models.Model):
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
class Book(models.Model):
title = models.CharField(max_length=64)
author = models.ForeignKeyField(Author, on_delete=models.CASCADE)

我有以下序列化程序:

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'title', 'author')
read_only_fields = ('id')

如果我查询我的书,一本书的数据看起来像:

{
"id": 1,
"title": "Book Title",
"author": 4
}

这就是我想要的,因为我返回了一系列书籍和一系列作者,并允许客户将所有内容连接起来。这是因为我有许多作者在书中反复出现。

然而,我想允许客户提交现有的作者id来创建新书,或者提交新作者的所有数据。例如:

现有作者新书的有效载荷:

{
"title": "New Book!",
"author": 7
}

或者,新作者新书的有效载荷:

{
"title": "New Book!",
"author": {
"first_name": "New",
"last_name": "Author"
}
}

然而,第二个版本将不会通过我的序列化程序中的数据验证步骤。有没有一种方法可以覆盖验证步骤,允许使用作者id或完整对象?然后,在我的序列化程序的create方法中,我可以检查类型,然后创建一个新的作者,获取其id,创建新书,或者只附加现有的id?

我认为以您想要的方式(使用一个字段author(是不可能的。

这只是因为一个序列化程序无法为一个字段处理两种不同的类型。

注意:我以前的说法可能是错的。

但是,以下是一个潜在的解决方案。您只需要使用不同的字段名称来创建新的作者。

class BookSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(
required=False,
queryset=Author.objects.all(),
)
author_add = AuthorSerializer(write_only=True, required=False)
class Meta:
model = Book
fields = ('id', 'title', 'author', 'author_add')
read_only_fields = ('id')
def create(self, validated_data):
author_add_data = validated_data.pop('author_add', None)
if author_add is not None:
validated_data['author'] = Author.objects.create(**author_add_data)
return super().create(validated_data)

注意:您需要处理同时发送authorauthor_add的情况。可能会在验证步骤中添加一个检查并引发ValidationError(如果两者都提供的话(。

主题外提示:您不需要说明状态read_only_fields = ('id',)-主键是只读的。

对于其他尝试这样做的人,以下是我最终得到的工作。

对于我的图书连载器,我做了以下操作:

class BookSerializer(serializers.ModelSerializer):
# make author a foreign key/id, read-only field so that it isn't
# processed by the validator, and on read returns just the id.
class Meta:
model = Book
fields = ('id', 'title', 'author')
read_only_fields = ('id', 'author',)
# override run_validation to validate our author
def run_validation(self, data):
# run base validation.  Since `author` is read_only, it will
# be ignored.
value = super(Book, self).run_validation(data)
# inject our validated author into the validated data
value['author'] = self.validate_author(data['author'])
return value
# Custom author validation
def validate_author(self, author):
errors = OrderedDict()
if isinstance(author, int): # if just a key, retrieve the author
try:
author_instance = Author.objects.get(pk=author)
except Author.DoesNotExist:
errors['author'] = "Author with pk {} does not exist.".format(author)
raise ValidationError(errors)
else: # if passed an author object...
author_serializer = AuthorSerializer(data=author, many=False)
author_serializer.is_valid(raise_exception=True)
author_instance = author_serializer.save()
return author_instance

我需要做更多的错误检查(例如-没有通过作者(,但它运行得很好-API的消费者可以提交作者id或序列化的作者对象来创建新作者。API本身只返回需要的id。

最新更新