【问题标题】:Django Rest Framework- can I allow pk id or full objects in a serializer's create method?Django Rest Framework-我可以在序列化程序的创建方法中允许 pk id 或完整对象吗?
【发布时间】:2019-10-24 18:47:23
【问题描述】:

假设我有以下模型:

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')

如果我随后查询我的书籍,A book 的数据如下所示:

{
    "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。想法?

【问题讨论】:

  • 您可以使用nested serializer 使第二个版本真正有效。
  • 正确,但是当我这样做时,我得到一个例外,即序列化程序需要第一个版本的字典。

标签: python django serialization django-rest-framework


【解决方案1】:

我相信不可能按照你想要的方式来做(使用一个字段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',) - 主键是只读的。

【讨论】:

    【解决方案2】:

    对于其他尝试这样做的人,这是我最终开始工作的内容。

    对于我的图书序列化器,我做了以下操作:

    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。

    【讨论】:

      猜你喜欢
      • 2021-09-08
      • 2018-05-24
      • 2019-01-29
      • 2016-10-22
      • 1970-01-01
      • 2020-03-05
      • 2015-02-10
      • 2015-08-15
      • 2013-02-02
      相关资源
      最近更新 更多