首页 > 解决方案 > Django 的 DRF ModelViewSet 中 CREATE 的可选 pk 参数

问题描述

我的一个模型有一个常规的 ModelViewSet,但我希望可以选择指定一个特定的 PK 来创建一个新实例。例如,如果我要发布:

{
    "name": "Name"
}

它会得到一个随机的pk。但是,如果我发布:

{
    "id": "123",
    "name": "Name"
}

我希望它具有指定的 pk (id)。

人类似,我所做的是将id字段添加到我的 ModelSerializer 中,如下所示:

class ConversationViewSet(viewsets.ModelViewSet):
    """
    List all conversations, or create new / edit existing product.
    """
    queryset = Conversation.objects.all()
    serializer_class = ConversationSerializer

class ConversationSerializer(serializers.ModelSerializer):
    id = serializers.CharField(required=False)  # Instead of serializer.ReadOnlyField()

    class Meta:
        model = Conversation
        fields = '__all__'

虽然这适用于该create方法,但它会导致updateandpartial_update的问题,现在 whereid是作为查询字符串参数的必需参数,并且在请求正文中像这样(来自文档):

update
PUT /conversations/{id}/
Update existing conversation.

Path Parameters
The following parameters should be included in the URL path.

Parameter       Description
id (required)   A unique value identifying this conversation.

Request Body
The request body should be a "application/json" encoded object, containing the following items.

Parameter       Description
id  
access_token    
username    
password    
app_user_id 
name    

当然,拥有两个同名的参数是不好的做法。例如,当使用requests和传递参数字典时,它不再起作用,因为它不知道我正在处理哪个参数。

我该如何解决这个问题,使id参数仅对该create方法是可选的,但所有其他方法(列表、读取、...)与默认情况下保持完全相同?

我的解决方案

根据 JPG 的回答,我将序列化程序修改为:

class ConversationSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.context['view'].action == 'create':
            self.fields['id'] = serializers.CharField(required=False)

    class Meta:
        model = Conversation
        fields = '__all__'

标签: djangodjango-rest-framework

解决方案


我认为这可以通过覆盖__init__()序列化程序的方法来实现。

class ConversationSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.context['view'].action == 'create':
            self.fields['id'] = serializers.CharField()
        else:
            self.fields['id'] = serializer.ReadOnlyField()

    class Meta:
        model = Conversation
        fields = '__all__'

推荐阅读