Django Rest Framework 部分更新

Posted

技术标签:

【中文标题】Django Rest Framework 部分更新【英文标题】:Django Rest Framework partial update 【发布时间】:2017-04-27 21:39:52 【问题描述】:

我正在尝试用Django Rest Framework 实现partial_update,但我需要澄清一下,因为我卡住了。

    为什么我们需要指定 partial=True? 据我了解,我们可以轻松地更新 partial_update 方法内的 Demo 对象。这样做的目的是什么?

    序列化变量的内部是什么?partial_update 方法中 serialized 变量的内部是什么?那是一个演示对象吗?幕后调用了什么函数?

    如何完成这里的实施?

视图集

class DemoViewSet(viewsets.ModelViewSet):
    serializer_class = DemoSerializer

    def partial_update(self, request, pk=None):
        serialized = DemoSerializer(request.user, data=request.data, partial=True)
        return Response(status=status.HTTP_202_ACCEPTED)

序列化器

class DemoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Demo
        fields = '__all__'

    def update(self, instance, validated_data):
        print 'this - here'
        demo = Demo.objects.get(pk=instance.id)
        Demo.objects.filter(pk=instance.id)\
                           .update(**validated_data)
        return demo

【问题讨论】:

在这里您可以找到有关视图、序列化程序等内部方法的更多信息。cdrf.co/3.9/rest_framework.generics/UpdateAPIView.html 【参考方案1】:

我之前也有和你一样的疑问,但是当我深入研究rest_framework的源代码时,我得到了以下发现,希望对您有所帮助:

对于问题 1。为什么我们需要指定 partial=True?

这个问题与HTTP verbs有关。

PUT:PUT 方法将目标资源的所有当前表示替换为请求负载。

PATCH:PATCH 方法用于对资源应用部分修改。

一般来说partial是用来检查客户端向视图提交数据时是否需要模型中的字段做字段验证。

例如,我们有一个像这样的Book 模型,请注意nameauthor_name 字段都是必填(非空且非空白)。

class Book(models.Model):
    name = models.CharField('name of the book', max_length=100)
    author_name = models.CharField('the name of the author', max_length=50)

# Create a new instance for testing
Book.objects.create(name='Python in a nut shell', author_name='Alex Martelli')

对于某些场景,我们可能只需要更新模型中的部分字段,例如,我们只需要更新Book中的name字段。因此,对于这种情况,客户端只会向视图提交具有新值的 name 字段。客户端提交的数据可能如下所示:

"pk": 1, name: "PYTHON IN A NUT SHELL"

但是您可能已经注意到我们的模型定义不允许author_name 为空。所以我们必须使用 partial_update 而不是 update。所以其余框架不会对请求数据中缺少的字段进行字段验证检查

出于测试目的,您可以为updatepartial_update 创建两个视图,您会更加理解我刚才所说的。

示例:

views.py
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import UpdateModelMixin
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book


class BookUpdateView(GenericAPIView, UpdateModelMixin):
    '''
    Book update API, need to submit both `name` and `author_name` fields
    At the same time, or django will prevent to do update for field missing
    '''
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

class BookPartialUpdateView(GenericAPIView, UpdateModelMixin):
    '''
    You just need to provide the field which is to be modified.
    '''
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def put(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
urls.py
urlpatterns = patterns('',
    url(r'^book/update/(?P<pk>\d+)/$', BookUpdateView.as_view(), name='book_update'),
    url(r'^book/update-partial/(?P<pk>\d+)/$', BookPartialUpdateView.as_view(), name='book_partial_update'),
)

要提交的数据

"pk": 1, name: "PYTHON IN A NUT SHELL"

当你将上面的json提交到/book/update/1/时,你会得到如下错误HTTP_STATUS_CODE=400:


  "author_name": [
    "This field is required."
  ]

但是当你将上面的 json 提交到/book/update-partial/1/ 时,你会得到 HTTP_STATUS_CODE=200 和下面的响应,


  "id": 1,
  "name": "PYTHON IN A NUT SHELL",
  "author_name": "Alex Martelli"

对于问题2。序列化变量的内部是什么?

serialized 是将模型实例包装为可序列化对象的对象。你可以使用这个序列化来生成一个带有 serialized.data 的纯 JSON 字符串。

对于问题 3。如何完成这里的实现?

我想你看了上面的答案就可以自己回答了,你应该知道什么时候用update,什么时候用partial_update

如果您还有任何问题,请随时提问。刚刚看了rest框架的部分源码,对某些术语可能理解的不是很深,有不对的地方请指出...

【讨论】:

当我尝试 partial_update 的代码时,它给了我一个错误提示“update() 方法未实现”... 嘿,我想在更新时获取字段中的数据,这些数据是我在创建时在休息浏览器中插入的,而且我正在使用您的部分方式,但我仍在获取字段必需的错误,我不知道它是否会出现,因为在浏览器中,嵌套序列化程序更新时字段中没有显示数据....实际上它显示的是用户的数据,而不是更新时的配置文件数据跨度> 为什么在我做 PATCH 时,在 pre_save 实例上填充了这个实例数据库数据?这不应该在保存时完成吗? update_fields 参数怎么样?它应该只包含要更新的字段。【参考方案2】:

对于部分更新 - PATCH http 方法

对于完整更新 - PUT http 方法

使用 DRF 进行更新时,您应该发送包含所有(必需)字段值的请求数据。当请求是通过 PUT http 方法时,至少是这种情况。据我了解,您想更新一个或至少不是所有模型实例字段。在这种情况下,使用 PATCH http 方法发出请求。 Django rest 框架 (DRF) 会开箱即用地处理它。

示例(使用令牌认证):

curl -i -X PATCH -d '"name":"my favorite banana"' -H "Content-Type: application/json" -H 'Authorization: Token <some token>'  http://localhost:8000/bananas/

【讨论】:

【参考方案3】:

如此简单,只需像这样覆盖序列化程序的 init 方法:

def __init__(self, *args, **kwargs):
    kwargs['partial'] = True
    super(DemoSerializer, self).__init__(*args, **kwargs)

【讨论】:

我想知道为什么这个答案没有得到更多的支持?它对我来说就像一种魅力【参考方案4】:

只是一个简短的说明,因为似乎没有人已经指出这一点:

serialized = DemoSerializer(request.user, data=request.data, partial=True)

DemoSerializer 的第一个参数应该是一个 Demo 实例,而不是一个用户(至少如果你像我一样使用 DRF 3.6.2)。

我不知道您要做什么,但这是一个可行的示例:

def partial_update(self, request, *args, **kwargs):
    response_with_updated_instance = super(DemoViewSet, self).partial_update(request, *args, **kwargs)
    Demo.objects.my_func(request.user, self.get_object())
    return response_with_updated_instance

我进行部分更新,然后调用 my_func 并传递当前用户和已更新的演示实例。

希望这会有所帮助。

【讨论】:

我想在更新时将数据显示到字段中你知道我们如何在嵌套序列化中做到这一点......因为否则我会得到空或空白字段,所以如果我编辑一个字段,它会向我显示一个错误,这些字段不能为空白或空......请帮助......谢谢【参考方案5】:

我遇到了一个问题,即我在 rest_framework 序列化程序中的多属性/字段验证正在处理 POST /resources/ 请求,但因 PATCH /resources/ 请求而失败。它在 PATCH 案例中失败了,因为它只在提供的 attrs 字典中查找值,而不是回退到 self.instance 中的值。添加一个方法get_attr_or_default 来执行该回退似乎有效:

class EmailSerializer(serializers.ModelSerializer):

    def get_attr_or_default(self, attr, attrs, default=''):
        """Return the value of key ``attr`` in the dict ``attrs``; if that is
        not present, return the value of the attribute ``attr`` in
        ``self.instance``; otherwise return ``default``.
        """
        return attrs.get(attr, getattr(self.instance, attr, ''))

    def validate(self, attrs):
        """Ensure that either a) there is a body or b) there is a valid template
        reference and template context.
        """
        existing_body = self.get_attr_or_default('body', attrs).strip()
        if existing_body:
            return attrs
        template = self.get_attr_or_default('template', attrs)
        templatecontext = self.get_attr_or_default('templatecontext', attrs)
        if template and templatecontext:
            try:
                render_template(template.data, templatecontext)
                return attrs
            except TemplateRendererException as err:
                raise serializers.ValidationError(str(err))
        raise serializers.ValidationError(NO_BODY_OR_TEMPLATE_ERROR_MSG)

【讨论】:

【参考方案6】:

我不知道为什么,但对我来说,解决它的唯一方法是覆盖 Serializer 类中的 validate 方法。

也许这与我使用 MongoDBDjongo

的事实有关
class DemoSerializer(serializers.ModelSerializer):
   def validate(self, attrs):
       self._kwargs["partial"] = True
       return super().validate(attrs)

【讨论】:

【参考方案7】:

你忘了serializer.save()

您可以通过以下方式完成它。 . .

class DemoViewSet(viewsets.ModelViewSet):
    serializer_class = DemoSerializer

    def partial_update(self, request, pk=None):
        serializer = DemoSerializer(request.user, data=request.data, partial=True)
        serializer.save()
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data)

此外,您不需要在序列化程序中重写更新方法。

【讨论】:

我认为首先你必须验证你的序列化器然后保存它。所以 serializer.is_valid() 将首先出现,然后是 serializer.save()

以上是关于Django Rest Framework 部分更新的主要内容,如果未能解决你的问题,请参考以下文章

记录对 django-rest-framework 的请求

Django REST framework 简介

django-rest-framework指南:Requests and Responses

DRF:如何将 django-rest-framework-jwt 集成到 Djoser

Django REST framework框架详解

django-rest-framework - 在可浏览的 API 中自动生成表单?