Reputation: 5023
I have three models:
class Customer(models.Model):
name = models.CharField(max_length=30)
class Order(models.Model):
customer = models.ForeignKey(Customer,on_delete=models.CASCADE)
class LineItem(models.Model):
order = models.ForeignKey(Order,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
Here is my test:
class CreateOrdersTest(APITestCase):
def setUp(self):
self.TEST_SIZE = 10
self.factory = APIRequestFactory()
self._setup_source()
self.data = self._build_data()
def _setup_source(self):
Customer.objects.create(name='test-customer')
def _build_data(self):
return [{'customer':'test-customer','lineitems':[{'name':'apples'},{'name':'oranges'}]} for x in range(self.TEST_SIZE)]
def test_post_orders(self):
request = self.factory.post('/create_orders',self.data)
response = create_orders(request)
response.render()
self.assertEqual(response.status_code,status.HTTP_201_CREATED)
so the post object looks like
[{'customer': 'test-customer', 'lineitems': [{'name': 'apples'}, {'name': 'oranges'}]}, .... ]
here are the serializers:
class BulkLineItemSerializer(serializers.ModelSerializer):
def create(self,validated_data):
lineitems = [LineItem(**validated_data) for item in validated_data]
return LineItem.objects.bulk_create(**validated_data)
class LineItemSerializer(serializers.ModelSerializer):
order = ModelObjectidField()
def create(self,validated_data):
return LineItem.objects.create(**validated_data)
class Meta:
model = LineItem
list_serializer_class = BulkLineItemSerializer
fields = ['name','order']
class BulkOrderSerializer(serializers.ListSerializer):
def create(self,validated_data):
orders = [Order(**item) for item in validated_data]
return Order.objects.bulk_create(orders)
class OrderSerializer(serializers.ModelSerializer):
customer = serializers.SlugRelatedField(slug_field='name',queryset=Customer.objects.all())
def create(self,validated_data):
return Order.objects.create(**validated_data)
class Meta:
model = Order
fields = ['customer']
list_serializer_class = BulkOrderSerializer
then here is that ModelObjectidField I use for the order object.
class ModelObjectidField(serializers.Field):
def to_representation(self, value):
return value.id
def to_internal_value(self, data):
return data
Because I am passing the actual object into the field like <Order(1)>, I just return it directly for the internal_value.
And finally here is my view. This is how I match the lineitems to the orders.
@api_view(['POST'])
def create_orders(request):
if request.method == 'POST':
fixed_orders = []
lineitems_matching = []
offset = 0
lineitems_data = []
for order in request.data:
order_lineitems = order['lineitems']
lineitems_data.extend(order_lineitems)
for item in order_lineitems:
lineitems_matching.append(offset)
offset += 1
fixed_orders.append(order)
orderSerializer = OrderSerializer(data=fixed_orders,many=True)
if orderSerializer.is_valid():
orders = orderSerializer.save()
fixed_lineitems = []
offset = 0
for lineitem_data in lineitems_data:
lineitem_data['order'] = orders[lineitems_matching[offset]]
fixed_lineitems.append(lineitem_data)
offset += 1
lineItemSerializer = LineItemSerializer(data=fixed_lineitems, many=True) # this line is getting the error
if lineItemSerializer.is_valid():
lineItemSerializer.save()
return Response(orderSerializer.data,status=status.HTTP_201_CREATED)
return Response(lineItemSerializer.errors,status=status.HTTP_400_BAD_REQUEST)
return Response(orderSerializer.errors,status=status.HTTP_400_BAD_REQUEST)
Currently I am getting this error:
(venv) alexmarshall@Alexs-MacBook-Pro spicy % ./manage.py test NestedOrderTest
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_post_orders (NestedOrderTest.tests.CreateOrdersTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/alexmarshall/src/GreenData/spicy/spicy/NestedOrderTest/tests.py", line 23, in test_post_orders
response = create_orders(request)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/decorators.py", line 50, in handler
return func(*args, **kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/spicy/NestedOrderTest/views.py", line 31, in create_orders
lineItemSerializer = LineItemSerializer(data=fixed_lineitems, many=True)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 121, in __new__
return cls.many_init(*args, **kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 158, in many_init
return list_serializer_class(*args, **list_kwargs)
File "/Users/alexmarshall/src/GreenData/spicy/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 115, in __init__
super().__init__(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'child'
----------------------------------------------------------------------
Ran 1 test in 0.010s
FAILED (errors=1)
I think the problem is that I am passing the lineitem serializer data that has the order itself. Here is what the fixed_lineitems data looks like:
[{'name': 'apples', 'order': <Order: Order object (1)>}, ... ]
Thats what I am using the custom field for, but It doesn't seem to like it? I don't really know what that TypeError means. Why would it be getting a child? But really Im just not sure how to setup the serializers in this case. Any help?
Edit: I changed the lineitem serializer to this and I believe its working!
class BulkLineItemSerializer(serializers.ListSerializer):
def create(self,validated_data):
lineitems = [LineItem(**item) for item in validated_data]
return LineItem.objects.bulk_create(lineitems)
Upvotes: 0
Views: 1019
Reputation: 119
Look at BulkLineItemSerializer
, it must inherit from ListSerializer
.
It is needed to handle passing list to it with many=True
https://www.django-rest-framework.org/api-guide/serializers/#listserializer
Like so:
class BulkLineItemSerializer(serializers.ListSerializer):
...
Upvotes: 2