0

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)
2
  • What do u want? Create order and line item for every dict in request data? do u need extra logic? Commented Mar 13, 2021 at 23:01
  • @kamilyrb lets say there are 2000 orders, each with 4 orders. I want to make 2 database calls. 1 for 2000 orders. The other for the 8000 lineitems each associated with the orders. The python code shows how I am manually assigning the orders to the lineitem objects before I attempt to save them in bulk. Does that clarify it? Commented Mar 14, 2021 at 1:46

1 Answer 1

2
+50

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):
    ...
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! With that change and the changes I show in the edit I believe its working again. I guess it was a bit of a typo but Im also not sure how you spotted that from the traceback error.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.