1

I am trying to create a serializer to create multiple products with one payload. Here's my attempt:

# models.py
class Product(models.Model):
    sku = models.CharField(unique=True)
    product_name = models.CharField()

# serializers.py
class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

    def create(self, validated_data):
        try:
            product = create_product( **validated_data)
        except:
            raise CustomException( detail = { "error" : "Unable to create product" } )

# views.py
class ProductCreate(generics.CreateAPIView):

    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get_serializer(self, *args, **kwargs):
        kwargs["many"] = True
        return super().get_serializer(*args, **kwargs)

So, this works well when the data are clean. Here's a response:

[ { "id" : 1, "sku" : "12345", "product_name" : "Product A" },
  { "id" : 2, "sku" : "56789", "product_name" : "Product B" }]

But if one of the products fails to be created, the error message is simply:

# response
{ "error" : "Unable to create product" }

What I'd like is the response to be a list, where each element corresponds to the product element in the request. For example, product A is created, but product B returns an error.

[ { "id" : 1, "sku" : "12345", "product_name" : "Product A" },
  { "error" : "Unable to create product" } ]

How can I get this type of response?

3 Answers 3

2

Also depends whether you want to have your serializer validate the data or not.

Because, serializers will fail if one of your records does not pass the validation.

If you want to add records which pass the validation but reports the ones which fail ,you can do following in your view's create function:

class ProductCreate(generics.CreateAPIView):

    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def create(self, request, *args, **kwargs):

        response = []

        for product in self.request.data:
            serializer = self.get_serializer(data=product)

            if serializer.is_valid(raise_exception=False):
                self.perform_create(serializer)
                response.append(serializer.data)
            else:
                response.append({"error" : "your error message"})

        return Response(response, status=201)

Note: in this case, you cannot set "many=True" when creating serializer.

Now the question is if you want to catch also errors when objects are written to database. In that case, you need to do it in serializer where the create-product is. But if saving object to db fails, you might have more serious issues, so i personally would leave to fail and to return error.

Sign up to request clarification or add additional context in comments.

1 Comment

This seems to work well. I just wrap self.perform_create() with a try/except to catch any error during the model saving.
1

You need two serializer class. One for just a product and other for product list.

class ProductSerializer(ModelSerializer)
    class meta:
        model = Product


class ProductListSerializer(Serializer):
    productList = ProductSerializer(many=True)

You must override create method in each serializer

1 Comment

Would this require the data to be in the form of { "productList" : [ product1, product2 ]} instead of my preferred [product1, product2]?
0

Try something like this,

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

    def create(self, validated_data):
        errors = []
        try:
            product = create_product(**validated_data)
        except:
            errors.append({"error": validated_data})
        if errors:  # If atleast one error occured
            raise CustomException(detail=errors)
        # return your default case



NOTE: I'm only emphasising the logic, I didn't tested the solution

Comments

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.