I am using Django REST framework for API and Angular SPA with Restangular to communicate with the API. Sometimes, I have to add more than one object using the API and I think I can send them together in an array and do this in one request.
I receive wrong input
error when I'm trying to add more than one object from the REST framework web interface. I am passing objects or array of objects like below:
// this { "text": "gdhg", },{ "text": "gdhg", },{ "text": "gdhg", }
// or this [{ "text": "gdhg", },{ "text": "gdhg", },{ "text": "gdhg", }]
But I receive ParseError. Where I am wrong and what do I have to change to fix this?
Another example that supports posting an array as well as posting a single object. Might be useful for anyone else looking for such an example.
class BookViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
"""
ViewSet create and list books
Usage single : POST
{
"name":"Killing Floor: A Jack Reacher Novel",
"author":"Lee Child"
}
Usage array : POST
[{
"name":"Mr. Mercedes: A Novel (The Bill Hodges Trilogy)",
"author":"Stephen King"
},{
"name":"Killing Floor: A Jack Reacher Novel",
"author":"Lee Child"
}]
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
search_fields = ('name','author')
def create(self, request, *args, **kwargs):
"""
#checks if post request data is an array initializes serializer with many=True
else executes default CreateModelMixin.create function
"""
is_many = isinstance(request.data, list)
if not is_many:
return super(BookViewSet, self).create(request, *args, **kwargs)
else:
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
I am not sure if the problem still exist. But the solution suggested by fiver did not work for me.
What works for me is overriding the get_serializer
method ONLY.
def get_serializer(self, instance=None, data=None, files=None, many=True, partial=False): return super(ViewName, self).get_serializer(instance, data, files, many, partial)
If you will notice I am setting default many=True
in arguments of get_serializer
.
Apart from that nothing is required. Overridng of create method is also not required.
Also if you are defining the pre_save and post_save method in the views, expects the list(iterable) as the argument(as you are posting the list) of method not just a single object.
def post_save(self, objects, *args, **kwargs): """ In the post_save, list of obj has been created """ for obj in objects: do_something_with(obj)
Here's an example for setting up bulk POSTing in a ListCreateAPIView using the Django REST Framework:
class SomethingList(generics.ListCreateAPIView):
model = Something
serializer_class = SomethingSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, many=True)
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The important part here is the many=True
argument to the get_serializer()
method. Then, to make Angular play nice with this, you can define a service factory as:
.factory('Something', ['$resource', function ($resource) {
return $resource(
"url_to_something",
{},
{
save: {
method: 'POST',
isArray: true
}
}
);
}])
Where the important part is the isArray: true
. If you want to preserve posting single JSON objects, you could change save
above to something like saveBulk
or similar.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With