How can I PUT/POST JSON data to a ListSerializer?
Django REST framework by default assumes that you are not dealing with bulk data creation, updates, or deletion. This is because 99% of people are not dealing with bulk data creation, and DRF leaves the other 1% to third-party libraries.
In Django REST framework 2.x and 3.x, a third party package exists for this.
Now, you are trying to do bulk creation but you are getting an error back that says
Invalid data. Expected a dictionary, but got list
This is because you are sending in a list of objects to create, instead of just sending in one. You can get around this a few ways, but the easiest is to just override get_serializer
on your view to add the many=True
flag to the serializer when it is a list.
def get_serializer(self, *args, **kwargs):
if "data" in kwargs:
data = kwargs["data"]
if isinstance(data, list):
kwargs["many"] = True
return super(MyViewSet, self).get_serializer(*args, **kwargs)
This will allow Django REST framework to know to automatically use the ListSerializer
when creating objects in bulk. Now, for other operations such as updating and deleting, you are going to need to override the default routes. I'm going to assume that you are using the routes provided by Django REST framework bulk, but you are free to use whatever method names you want.
You are going to need to add methods for bulk PUT
and PATCH
to the view as well.
from rest_framework.response import Response
def bulk_update(self, request, *args, **kwargs):
partial = kwargs.pop("partial", False)
queryset = self.filter_queryset(self.get_queryset))
serializer = self.get_serializer(instance=queryset, data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
def partial_bulk_update(self, *args, **kwargs):
kargs["partial"] = True
return super(MyView, self).bulk_update(*args, **kwargs)
This won't work out of the box as Django REST framework doesn't support bulk updates by default. This means you also have to implement your own bulk updates. The current code will handle bulk updates as though you are trying to update the entire list, which is how the old bulk updating package previously worked.
While you didn't ask for bulk deletion, that wouldn't be particularly difficult to do.
def bulk_delete(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
self.perform_delete(queryset)
return Response(status=204)
This has the same effect of removing all objects, the same as the old bulk plugin.
None of this code was tested. If it doesn't work, consider it as a detailed example.
Related videos on Youtube
petroleyum
Updated on September 26, 2022Comments
-
petroleyum over 1 year
I'm reading about customizing multiple update here and I haven't figured out in what case the custom
ListSerializer
update method is called. I would like to update multiple objects at once, I'm not worried about multiple create or delete at the moment.From the example in the docs:
# serializers.py class BookListSerializer(serializers.ListSerializer): def update(self, instance, validated_data): # custom update logic ... class BookSerializer(serializers.Serializer): ... class Meta: list_serializer_class = BookListSerializer
And my ViewSet
# api.py class BookViewSet(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
And my url setup using
DefaultRouter
# urls.py router = routers.DefaultRouter() router.register(r'Book', BookViewSet) urlpatterns = patterns('', url(r'^api/', include(router.urls)), ...
So I have this set up using the
DefaultRouter
so that/api/Book/
will use theBookSerializer
.Is the general idea that if I POST/PUT/PATCH an array of JSON objects to
/api/Book/
then the serializer should switch over toBookListSerializer
?I've tried POST/PUT/PATCH JSON data list to this
/api/Book/
that looks like:[ {id:1,title:thing1}, {id:2, title:thing2} ]
but it seems to still treat the data using
BookSerializer
instead ofBookListSerializer
. If I submit via POST I getInvalid data. Expected a dictionary, but got list
. and if I submit via PATCH or PUT then I get aMethod 'PATCH' not allowed
error.Question: Do I have to adjust the
allowed_methods
of theDefaultRouter
or theBookViewSet
to allow POST/PATCH/PUT of lists? Are the generic views not set up to work with theListSerializer
?I know I could write my own list deserializer for this, but I'm trying to stay up to date with the new features in DRF 3 and it looks like this should work but I'm just missing some convention or some option.
-
petroleyum over 9 yearsThanks. Fixed that. Now I'm submitting a list to
/api/Book/
. If I submit via POST I getInvalid data. Expected a dictionary, but got list.
and if I submit via PATCH or PUT then I get aMethod 'PATCH' not allowed.
. I'm using theDefaultRouter
and aModelViewSet
if that helps. -
Kevin Brown-Silva over 9 yearsI've updated the answer to address your question in detail. Hopefully it covers the multiple parts to your question. With any luck, the third party package will be updated to support 3.0 in the near future.
-
petroleyum over 9 yearsAwesome, thank you. I guess my question could have been boiled down to "It looks like DRF has some default bulk update behavior now with 3.0?" and the answer is "Not quite." Your code example is exactly what I needed, I'll test it out and update if necessary.
-
Quentin Donnellan over 9 yearsThanks a bunch for this Kevin - this fixes things for us here: github.com/burke-software/django-sis/commit/…
-
miki725 about 9 yearsUpdated the answer that DRF-bulk now support both DRF2 and DRF3! - github.com/miki725/django-rest-framework-bulk
-
Sassan about 7 yearsDRF-bulk is not maintained anymore. What's current canonical solution for this?
-
Kevin Brown-Silva about 7 years@sassan up until 3.4.x it worked fine, you may want to consider creating a ticket with them about any recent issues
-
Sassan about 7 yearsDidn't try it when I saw last commit is 2 years ago. Now I'll try it. Thanks for quick response.
-
Greg Holst over 4 yearsI found a solution that's less complicated (at least for me as a Django beginner) stackoverflow.com/a/59756993/7392069
-
cavalcantelucas almost 3 yearsThan you for the excelent explanation! I'd wish to vote twice on it!