182π
I when digging into the source code of rest_framework and got the following findings:
For question 1. Why do we need to specify partial=True?
This question is related to HTTP verbs.
PUT: The PUT method replaces all current representations of the target resource with the request payload.
PATCH: The PATCH method is used to apply partial modifications to a resource.
Generally speaking, partial
is used to check whether the fields in the model is needed to do field validation when client submitting data to the view.
For example, we have a Book
model like this, pls note both of the name
and author_name
fields are mandatory (not null & not blank).
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')
For some scenarios, we may only need to update part of the fields in the model, e.g., we only need to update name
field in the Book
. So for this case, client will only submit the name
field with new value to the view. The data submit from the client may look like this:
{"pk": 1, name: "PYTHON IN A NUT SHELL"}
But you may have notice that our model definition does not allow author_name
to be blank. So we have to use partial_update
instead of update
. So the rest framework will not perform field validation check for the fields which is missing in the request data.
For testing purpose, you can create two views for both update
and partial_update
, and you will get more understanding what I just said.
Example:
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'),
)
Data to submit
{"pk": 1, name: "PYTHON IN A NUT SHELL"}
When you submit the above json to the /book/update/1/
, you will got the following error with HTTP_STATUS_CODE=400:
{
"author_name": [
"This field is required."
]
}
But when you submit the above json to /book/update-partial/1/
, you will got HTTP_STATUS_CODE=200 with following response,
{
"id": 1,
"name": "PYTHON IN A NUT SHELL",
"author_name": "Alex Martelli"
}
For question 2. What is inside of serialized variable?
serialized
is a object wrapping the model instance as a serialisable object. and you can use this serialized to generate a plain JSON string with serialized.data
.
For question 3. How would one finish the implementation here?
I think you can answer yourself when you have read the answer above, and you should have known when to use update
and when to used partial_update
.
If you still have any question, feel free to ask. I just read part of the source code of the rest framework, and may have not understand very deeply for some terms, and please point it out when it is wrongβ¦
50π
For partial update β PATCH http method
For full update β PUT http method
When doing an update with DRF, you are supposed to send request data that includes values for all (required) fields. This is at least the case when the request is via the PUT http method. From what I understand, you want to update one or at least not all model instance fields. In this case make a request with the PATCH http method. Django rest framework (DRF) will take care of it out of the box.
Example (with token auth):
curl -i -X PATCH -d '{"name":"my favorite banana"}' -H "Content-Type: application/json" -H 'Authorization: Token <some token>' http://localhost:8000/bananas/
- [Django]-What is the easiest way to clear a database from the CLI with manage.py in Django?
- [Django]-Django-tables2: How to use accessor to bring in foreign columns?
- [Django]-Migrating Django fixtures?
16π
So simple, just override init method of your serializer like that:
def __init__(self, *args, **kwargs):
kwargs['partial'] = True
super(DemoSerializer, self).__init__(*args, **kwargs)
- [Django]-How to understand lazy function in Django utils functional module
- [Django]-How to Unit test with different settings in Django?
- [Django]-Django: Reverse for 'detail' with arguments '('',)' and keyword arguments '{}' not found
5π
Just a quick note as it seems that nobody has already pointed this out:
serialized = DemoSerializer(request.user, data=request.data, partial=True)
The first argument of DemoSerializer should be a Demo instance, not a user (at least if you use DRF 3.6.2 like me).
I donβt know what you are trying to do, but this is a working example:
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
I do the partial update and then I do other things calling my_func and passing the current user and the demo instance already updated.
Hope this helps.
- [Django]-AssertionError: database connection isn't set to UTC
- [Django]-How to display the current year in a Django template?
- [Django]-Django REST Framework: adding additional field to ModelSerializer
0π
I had an issue where my multi-attribute/field validation in a rest_framework serializer was working with a POST /resources/ request but failing with a PATCH /resources/ request. It failed in the PATCH case because it was only looking for values in the supplied attrs
dict and not falling back to values in self.instance
. Adding a method get_attr_or_default
to do that fallback seems to have worked:
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)
- [Django]-CSV new-line character seen in unquoted field error
- [Django]-In django do models have a default timestamp field?
- [Django]-Auto-create primary key used when not defining a primary key type warning in Django
0π
I donβt know why, but for me, the only way to solve it was to override the validate
method in the Serializer
class.
Maybe itβs related to the fact that Iβm using MongoDB with Djongo
class DemoSerializer(serializers.ModelSerializer):
def validate(self, attrs):
self._kwargs["partial"] = True
return super().validate(attrs)
- [Django]-How to run Django's test database only in memory?
- [Django]-How do I get user IP address in Django?
- [Django]-Django-DB-Migrations: cannot ALTER TABLE because it has pending trigger events
-8π
You forgot serializer.save()
You can finish it the following way . . .
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)
Also, you shouldnβt need to override the update method in the serializer.
- [Django]-Annotate with latest related object in Django
- [Django]-Does django with mongodb make migrations a thing of the past?
- [Django]-How to assign items inside a Model object with Django?