如何使用 Django Rest Framework 修补单个字段?

How to PATCH a single field using Django Rest Framework?

我有一个包含许多字段的模型 'MyModel',我想使用 PATCH 方法更新一个字段 'status'。我正在使用基于 class 的视图。有什么办法可以实现PATCH吗?

序列化程序允许 partial updates by specifying partial=True when initializing the serialzer. This is how PATCH requests are handled by default in the generic views

serializer = CommentSerializer(comment, data=request.data, partial=True)

这将允许您更新序列化程序中的单个字段,或者如果需要,可以更新所有字段,而不受标准 PUT 请求的任何限制。

它似乎开箱即用。在您的浏览器 API 上,导航到模型详细信息页面,在 HTML Form 选项卡旁边的底部单击 Raw data,删除 JSON 字符串中除 ID 字段之外的所有内容,然后您要更改的字段,然后单击 PATCH。执行部分 PATCH 更新。

我正在使用 djangorestframework==3.2.4,无需对我的 ViewSet 和序列化器执行任何操作即可启用此功能。

在这个例子中,我们正在更新模型的 bool status_field 字段,我使用的是 jquery 2.2.1。将以下内容添加到 <head>:

<script src="{% static 'my_app/jquery.min.js' %}"></script>
<script>
$(document).ready(function(){
    var chk_status_field = $('#status_field');
    chk_status_field.click(function(){
        $.ajax({url: "{% url 'model-detail' your_rendering_context.id %}",
            type: 'PATCH', timeout: 3000, data: { status_field: this.checked }
        })
        .fail(function(){
            alert('Error updating this model instance.');
            chk_status_field.prop('checked', !chk_status_field.prop('checked'));
        });
    });
});
</script>

然后在 <form>:

<input type="checkbox" id="status_field" {% if your_rendering_context.status_field %} 
    checked {% endif %} >

我选择允许更改复选框,然后在失败的情况下恢复它。但是您可以将 click 替换为 mousedown 以仅在 AJAX 调用成功后更新复选框值。我认为这会导致人们反复点击慢速连接的复选框。

正如 stated you could use the partial=True, which 所阐明的那样。

我只想更正它们并说您可以自由使用泛型,具体取决于您使用的 HTTP 方法:

  • 如果您按照要求使用 PATCH HTTP 方法,您可以开箱即用。您可以看到 UpdateModelMixin 代码 partial_update:

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    
  • 对于不同于 PATCH 的任何 HTTP 方法,这可以通过重写 get_serializer 方法来实现,如下所示:

    def get_serializer(self, *args, **kwargs):
        kwargs['partial'] = True
        return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)
    

这会将序列化程序创建为部分序列化程序,其余的泛型将像魅力一样工作,而无需对 update/partial_update 机制进行任何手动干预。

幕后花絮

我使用了泛型:generics.UpdateAPIView,它使用了具有以下代码的 UpdateModelMixin

def update(self, request, *args, **kwargs):
    partial = kwargs.pop('partial', False)
    instance = self.get_object()
    serializer = self.get_serializer(instance, data=request.data, partial=partial)
    …

因此,如果您覆盖 get_serializer 函数,您实际上可以覆盖部分参数并强制其为真。

请注意,如果您只希望它部分用于某些 HTTP 方法,这会使这种方法更加困难。

我正在使用 djangorestframework==3.5.3

我为此纠结了一段时间,但它是使用通用视图或通用视图与混入的组合的非常简单的实现。

在使用通用更新视图(generics.UpdateAPIView)的情况下,只需使用以下代码,确保请求类型为 PATCH:

class UpdateUser(generics.UpdateAPIView):

    queryset = User.objects.all()
    serializer_class = UserSerializer

没有别的了!

如果您将更新混合 (mixins.UpdateModelMixin) 与通用视图 (generics.GenericAPIView) 结合使用,请使用以下代码,确保请求类型为 PATCH:

class ActivateUser(mixins.UpdateModelMixin, generics.GenericAPIView):

    serializer_class = UserSerializer
    model = User
    lookup_field = 'email'

    def get_queryset(self):
        queryset = self.model.objects.filter(active=False)
        return queryset

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

第二个例子比较复杂,展示了如何覆盖queryset和lookup field,但是你要注意的代码是patch函数。

如果有人仍然计划使用 ModelSerializer 找到一个简单的解决方案而不改变您的大部分观点,您可以子类化 ModelSerializer 并让您的所有 ModelSerializer 继承自它.

class PatchModelSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        kwargs['partial'] = True
        super(PatchModelSerializer, self).__init__(*args, **kwargs)

class ArticleSerializer(PatchModelSerializer):
    class Meta:
        model = Article