"non_field_errors": [ "Invalid data. Expected a dictionary, but got list." ] in django rest framework while calling api in postman

Viewed 10962

It was working fine when I created order object from orderitem objects using validated.pop() function. Now, I have another model for BillingDetails that needs to be created as well which has a foreign key to Order model.

Now front end is sending chunks of data which contains orderitems data and also billingdetails at the same time. And I have to create Order object creating Orderitem objects and BillingDetails object at the same time.

I have tried writing this but got error on api call on Postman.

enter image description here

My models:

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)    
    start_date = models.DateTimeField(auto_now_add=True)
    ordered_date = models.DateTimeField()
    ordered = models.BooleanField(default=False)   
    def __str__(self):
        return self.user.email

class OrderItem(models.Model):    
    order = models.ForeignKey(Order,on_delete=models.CASCADE, blank=True,null=True,related_name='order_items')
    item = models.ForeignKey(Product, on_delete=models.CASCADE,blank=True, null=True)
    quantity = models.IntegerField(default=1)


    def __str__(self):
        return f"{self.quantity} items of {self.item} of {self.order.user}"


class BillingDetails(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    order = models.OneToOneField(Order, on_delete=models.CASCADE, blank=True, null=True, related_name='billing_details')
    first_name = models.CharField(max_length=50,blank=True,null=True)
    last_name = models.CharField(max_length=50, blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    phone = models.CharField(max_length=50,blank=True,null=True)
    country = models.CharField(max_length=50,blank=True,null=True)
    city = models.CharField(max_length=50,blank=True,null=True)
    address = models.CharField(max_length=100,blank=True,null=True)
    postal = models.CharField(max_length=50,blank=True,null=True)

    def __str__(self):
        return self.address

My view:

class AddtoOrderItemView(ListCreateAPIView):
    permission_classes = [IsAuthenticated]
    queryset = OrderItem.objects.all()
    serializer_class = OrderSerializer

My serializer (Initially):

class OrderSerializer(serializers.ModelSerializer):    
    order_items = OrderItemSerializer(many=True)
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
    class Meta:
        model = Order
        fields = ['id','user','start_date', 'ordered_date', 'ordered', 'order_items']
        # depth = 1
    

     def create(self, validated_data):
         user = self.context['request'].user
         order_items = validated_data.pop('order_items')
         order = Order.objects.create(user=user,**validated_data)
         for order_items in order_items:
             OrderItem.objects.create(order=order,**order_items)
         return order

It was working fine until now, I was able to order object while creating orderitems at the same time. Now when I popped billing deatils data and tried to create one, I got this Expected a dictionary, but got list. on Postman.

My serializer (Now)

class OrderSerializer(serializers.ModelSerializer):
    billing_details = BillingDetailsSerializer(many=False)
    order_items = OrderItemSerializer(many=True)
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
    class Meta:
        model = Order
        fields = ['id','user','start_date', 'ordered_date', 'ordered', 'order_items','billing_details']
        # depth = 1

    
    def create(self, validated_data):
        user = self.context['request'].user
        order_items = validated_data.pop('order_items')
        billing_details = validated_data.pop('billing_details')
        order = Order.objects.create(user=user,**validated_data)
        BillingDetails.objects.create(user=user,**billing_details)
        for order_items in order_items:
            OrderItem.objects.create(order=order,**order_items)
        return order

How to resolve this??

1 Answers

Based on your Postman screenshot, you're sending a list (denoted by []).

"billing_details": [
    {
     "user":34,
     "first_name":"Mylo",
     "last_name":"Don"
    }
]

If you remove the brackets so your POST request contains a dictionary (denoted by {}) then you shouldn't see this error anymore.

"billing_details": {
     "user":34,
     "first_name":"Mylo",
     "last_name":"Don"
}

If you would like to be able to send multiple requests to billing_details at a time then you can update your serializers.py like so:

class OrderSerializer(serializers.ModelSerializer):
    billing_details = BillingDetailsSerializer(many=True)

This will also make your original POST request work since passing the many=True parameter causes the serializer to expect a list, in this case it will be a list of length 1.

Related