I have a very simple model and its related serializer and views:
class Page(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=255)
pub_date = models.DateTimeField(default=timezone.now)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
fields = ('user', 'title', 'pub_date')
class PageViewSet(viewsets.ModelViewSet):
queryset = Page.objects.all()
serializer_class = PageSerializer
Now I can post like this:
{
"user": 1,
"title": "Lorem ipsum"
}
This works fine. But I would like to post multiple objects like this:
[
{
"user": 1,
"title": "Lorem ipsum one"
},
{
"user": 1,
"title": "Lorem ipsum two"
}
]
But this gives me an error:
"non_field_errors": [
"Invalid data. Expected a dictionary, but got list."
]
So to accept multiple objects I modified the view as per the doc:
class PageViewSet(viewsets.ModelViewSet):
queryset = Page.objects.all()
serializer_class = PageSerializer(queryset, many=True)
But I am getting an error:
TypeError at /api/blog/pages/
'ListSerializer' object is not callable
What am I missing here?
A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as . get() or . post() , and instead provides actions such as . list() and . create() .
To create multiple model instances with the Python Django Rest Framework, we can create a serialize with many set to True . to call the super class' __init__ method with the many argument set to many . If it's True , then our serializer can accept multiple model instances.
APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.
The root QuerySet provided by the Manager describes all objects in the database table. Usually, though, you'll need to select only a subset of the complete set of objects. The default behavior of REST framework's generic list views is to return the entire queryset for a model manager.
You had it set up correctly before, the serializer_class should point to the class like this:
serializer_class = PageSerializer
You were right on the error, it doesn't like receiving multiple objects in the POST. One way to fix it would be to override the get_serializer method n the view, adding in many=True there.
Something like:
def get_serializer(self, *args, **kwargs):
if "data" in kwargs:
data = kwargs["data"]
# check if many is required
if isinstance(data, list):
kwargs["many"] = True
return super(PageViewSet, self).get_serializer(*args, **kwargs)
To answer your question in the comments: isinstance()
is a Python method to check the type of an object. In this case, it's a list
when passing in multiple objects, so we just check what type data
is and set many=true
if it's a list
.
DRF docs regarding get_serializer (under methods)
Python docs regarding isinstance
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