Частичное обновление 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
Ответы
Ответ 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)
Кроме того, вам не нужно переопределять метод обновления в сериализаторе.