Django Rest Framework部分更新


72

我正在尝试partial_update使用Django Rest Framework进行实施,但由于我被卡住了,因此需要澄清一下。

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

  2. 序列化变量里面有什么?方法变量
    内部是什么?那是一个演示对象吗?在后台调用什么功能?serializedpartial_update

  3. 一个人如何在这里完成实施?

视图集

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/

Answers:


111

我之前曾遇到过与您相同的问题,但是当我深入研究rest_framework的源代码时,我得到了以下发现,希望对您有所帮助:

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

这个问题与HTTP动词有关

PUT:PUT方法用请求有效载荷替换目标资源的所有当前表示形式。

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

通常,partial用于在客户向视图提交数据时检查模型中的字段是否需要进行字段验证。

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

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

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

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

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

出于测试目的,您可以为update和创建两个视图partial_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是将模型实例包装为可序列化对象的对象。并且您可以使用此序列化生成带有的纯JSON字符串serialized.data

对于问题3。如何在这里完成实施?

我认为您在阅读完上述答案后就可以回答自己,并且应该知道何时使用update和何时使用partial_update

如果您还有任何疑问,请随时提问。我只是阅读了rest框架的部分源代码,可能对某些术语不太了解,请在错误的地方指出来...


当我尝试partial_update的代码时,它给我一个错误,提示“未实现update()方法” ...
dpstart

嘿,我想在更新时获取字段中的数据,我在休息浏览器中创建时插入了该字段,并且我正在使用您的部分方式,但是我仍在获取字段必填错误,我不知道如果是因为在浏览器中嵌套的序列化程序更新时字段中未显示任何数据而已....实际上,这是在显示我的用户数据,而不是更新时的个人资料数据
Nikhil Bhardwaj,

为什么在我执行PATCH时,在pre_save实例中填充了该实例数据库数据?这不应该保存吗?
mrroot5

怎么样的update_fields参数?它应该仅包括要更新的字段。
Enix

37

对于部分更新- PATCH HTTP方法

对于完全更新- PUT HTTP方法

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

示例(带有令牌auth):

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

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并传递当前用户和已更新的演示实例。

希望这可以帮助。


我想在更新时将数据显示到字段中,您知道我们如何在嵌套序列化中执行此操作吗……因为否则我将得到null或空白字段,因此如果我编辑一个字段,它将显示给我错误,这些字段不能为空或为空。...请帮助....谢谢您
Nikhil Bhardwaj

4

如此简单,只需重写序列化器的init方法,如下所示:

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

1
我不知道为什么这个答案没有得到更多的支持?它对我来说就像一种魅力
lingxiao

0

我遇到一个问题,我的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)

-5

你忘了 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)

另外,您不需要覆盖序列化程序中的update方法。


10
我认为您首先必须验证序列化器,然后再保存它。因此,serializer.is_valid()将首先出现,然后是serializer.save()
CodeTarsier
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.