'Why is Django serializer converting object to string? ('Invalid data. Expected a dictionary, but got str.')
I'm using django-treebeard to bulk create (load_bulk) a bunch of items nested into a list in this format:
items = [
{
"data": {"item_id": i1.id},
},
{
"data": {"item_id": i2.id},
},
{
"data": {"item_id": i3.id},
},
]
payload = {"list_id": l1.id, "items": items}
Here is my models.py:
class List(MP_Node):
item = models.ForeignKey(Item, null=True, blank=True)
My views.py:
class ListParentBulkCreateView(generics.CreateAPIView):
queryset = List.objects.all()
serializer_class = ListBulkCreateSerializer
permission_classes = [AllowAny]
My serializers.py:
class ListBulkCreateSerializer__data(serializers.ModelSerializer):
item_id = serializers.UUIDField(required=True)
class Meta:
model = List
fields = ["item_id"]
class ListBulkCreateSerializer_itemsWrapper(serializers.ModelSerializer):
data = ListBulkCreateSerializer__data()
class Meta:
model = List
fields = ["data"]
class ListBulkCreateSerializer(serializers.ModelSerializer):
list_id = serializers.UUIDField(required=True)
items = serializers.ListField(
child=ListBulkCreateSerializer_itemsWrapper(), write_only=True
)
class Meta:
model = List
fields = ["list_id", "items"]
def create(self, validated_data):
# My code fails on is_valid() so
# this doesn't even run
list_id = validated_data["list_id"]
list = List.objects.get(id=list_id)
items = validated_data("items")
# create tree
List.load_bulk(items, parent=list)
return {"list_id": list_id}
But I'm getting a 400 error when I try to create via a serializer. The serializer.error is:
{'parents': {0: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}, 1: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}, 2: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}}}
When I breakdown the CreateAPIView's create method, it reveals that the serializer is converting my list of objects into a list of strings:
class ListParentBulkCreateView(generics.CreateAPIView):
...
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
print(serializer.is_valid())
print(serializer.errors)
data = serializer.data
print("0", data, type(data))
print("1", data["parents"], type(data["parents"]))
print("2", data["parents"][0], type(data["parents"][0]))
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
)
Returns:
False
{'parents': {0: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}, 1: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}, 2: {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}}}
0 <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
1 <class 'list'>
2 <class 'str'>
Why is this happening? I can confirm my initial payload is correct:
print(payload["items"][0] # <class 'dict'>
Edit:
So after a bunch of trial and error, I got my code working if I json.dump my initial payload then json.load the request.data in my CreateAPIView create method....
But why on earth is this necessary?
items = [
{
"data": {"item_id": str(i1.id)},
},
{
"data": {"item_id": str(i2.id)},
},
{
"data": {"item_id": str(i3.id)},
},
]
payload = {"list_id": str(l1.id), "items": items}
views.py
class ListParentBulkCreateView(generics.CreateAPIView):
...
def create(self, request, *args, **kwargs):
data = json.loads(request.data)
serializer = self.get_serializer(data=data)
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
)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
