根据请求类型更改Django REST Framework ModelSerializer中的字段?

him*_*229 14 django django-serializer django-rest-framework

考虑这种情况,我有一个BookAuthor模型.

serializers.py

class AuthorSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Author
        fields = ('id', 'name')

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)

    class Meta:
        model = models.Book
        fields = ('id', 'title', 'author')
Run Code Online (Sandbox Code Playgroud)

viewsets.py

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
Run Code Online (Sandbox Code Playgroud)

如果我发送GET书籍请求,这很有用.我得到一个带有嵌套序列化程序的输出,其中包含书籍详细信息和嵌套作者详细信息,这就是我想要的.

然而,当我要创建/更新的一本书,我要送一POST/ PUT/ PATCH与作者而不只是它们的ID的嵌套的细节.我希望能够通过指定作者ID而不是整个作者对象来创建/更新书籍对象.

所以,我的序列化器看起来像这样的GET请求

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)

    class Meta:
        model = models.Book
        fields = ('id', 'title', 'author')
Run Code Online (Sandbox Code Playgroud)

和我的串行器看起来像这样的POST,PUT,PATCH请求

class BookSerializer(serializers.ModelSerializer):
    author = PrimaryKeyRelatedField(queryset=Author.objects.all())

    class Meta:
        model = models.Book
        fields = ('id', 'title', 'author')
Run Code Online (Sandbox Code Playgroud)

我也不想为每种类型的请求创建两个完全独立的序列化器.我想修改一下该author字段BookSerializer.

最后,是否有更好的方法来完成这一切?

Jad*_*eda 6

恕我直言,多个序列化器只会产生越来越多的混乱.

相反,我更喜欢以下解决方案:

  1. 不要更改您的视图集(保留默认值)
  2. 在序列化程序中添加.validate()方法; 以及其他必需的.create或.update()等.这里,真正的逻辑将进入validate()方法.根据请求类型,我们将根据序列化程序的要求创建validated_data dict.

我认为这是最干净的方法.

DRF中查看我的类似问题和解决方案:允许GET请求中的所有字段,但将POST限制为仅一个字段


jmo*_*moz 6

DRF的一个功能是您可以动态更改序列化器上的字段http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

我的用例:在GET上使用slug字段,这样我们就可以看到很好的关系代表,但是在POST/PUT上切换回经典的主键更新.将序列化器调整为以下内容:

class FooSerializer(serializers.ModelSerializer):
    bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all())

    class Meta:
        model = models.Foo
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(FooSerializer, self).__init__(*args, **kwargs)

        try:
            if self.context['request'].method in ['POST', 'PUT']:
                self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all())
        except KeyError:
            pass
Run Code Online (Sandbox Code Playgroud)

KeyError有时会在没有请求的情况下抛出代码初始化,可能是单元测试.

享受并负责任地使用.


Aar*_*ier 5

您正在寻找get_serializer_class上的方法ViewSet。这允许您切换要使用的序列化器的请求类型。

from rest_framework import viewsets

class MyModelViewSet(viewsets.ModelViewSet):

    model = MyModel
    queryset = MyModel.objects.all()

    def get_serializer_class(self):
        if self.action in ('create', 'update', 'partial_update'):
            return MySerializerWithPrimaryKeysForCreatingOrUpdating
        else:
            return MySerializerWithNestedData
Run Code Online (Sandbox Code Playgroud)