Подтвердить что ты не робот

Частичное обновление Django Rest Framework

Я пытаюсь реализовать partial_update с Django Rest Framework, но мне нужно уточнить, потому что я застрял.

  • Зачем нам нужно указывать partial = True?
    По моему мнению, мы могли бы легко обновить объект Demo внутри метода partial_update. Какова цель этого?

  • Что внутри сериализованной переменной?
    Что находится внутри переменной serialized в методе partial_update? Это объект Demo? Какую функцию вызывается за кулисами?

  • Как можно закончить реализацию здесь?

Viewset

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)

Serializer

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
4b9b3361

Ответ 1

У меня есть те же вопросы, что и у вас, но когда я копаюсь в исходном коде rest_framework, я получаю следующие выводы, надеюсь, это поможет:

На вопрос 1)

Этот вопрос связан с глаголами HTTP.

PUT: метод PUT заменяет все текущие представления целевого ресурса полезной нагрузкой запроса.

PATCH: метод PATCH используется для частичного изменения ресурса.

Вообще говоря, partial используется для проверки того, нужны ли поля в модели для проверки полей, когда клиент отправляет данные в представление.

Например, у нас есть модель Book подобная этой, author_name, обратите внимание, что оба поля name и author_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')

В некоторых случаях нам может потребоваться обновить только часть полей в модели, например, нам нужно только обновить поле 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.

Если у вас все еще есть вопросы, не стесняйтесь спрашивать. Я только что прочитал часть исходных одесов фреймворка отдыха, и, возможно, не очень хорошо понял некоторые термины, и, пожалуйста, укажите, когда это неправильно...

Ответ 2

Для частичного обновления - PATCH http method

Для полного обновления - PUT http method

При выполнении обновления с помощью DRF вы должны отправлять данные запроса, которые включают значения для всех (обязательных) полей. Это, по крайней мере, тот случай, когда запрос выполняется с помощью метода PUT http. Насколько я понимаю, вы хотите обновить один или, по крайней мере, не все экземпляры экземпляров модели. В этом случае сделайте запрос с помощью метода http PATCH. Django rest framework (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/

Ответ 3

Просто быстрая заметка, так как кажется, что об этом никто не говорил:

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, и передачу текущего пользователя и демонстрационного экземпляра, уже обновленного.

Надеюсь это поможет.

Ответ 4

У меня была проблема, когда моя проверка нескольких атрибутов/полей в сериализаторе 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

Просто переопределите метод init вашего сериализатора следующим образом:

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

Ответ 6

Ты забыл 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)

Кроме того, вам не нужно переопределять метод обновления в сериализаторе.