C-Bizz
C-Bizz

Reputation: 654

Error in making POST request to django API

From post man, I'm trying to make POST request to an order API created using django restframework but I'm getting the following error:

 product = Product.objects.get(id=i['product'])
TypeError: string indices must be integers

The specific point where the error is located is specified in the error but I find difficulty constructing VALID json for the request body. Here is how I'm making the request on postman:

{
    "orderItems":{
        "product": {"name":"abc", "brand":"def", "image"www.example.com/img", "description":"xyz", "price":"50"},
        "qty":"2",
        "price":"200"
    },
    "shippingAddress": {
        "address":"abc", "city":"B", "postalCode":"12345",
    },
    
    "paymentMethod":"monify",
    "itemPrice":"100"
}

Here is the program:

class Product(models.Model):
    category = models.CharField(max_length=50, choices=Categories.choices, default=Categories.medications)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="user_product", null=True)
    name = models.CharField(max_length=150)
    brand = models.CharField(max_length=255, default="brand")
    productClass = models.CharField(max_length=50, null=True, blank=True)
    image = models.ImageField(upload_to="images/products/")
    label = models.CharField(max_length=254, default='', blank=True, null=True)
    price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
    stock = models.IntegerField(null=True, blank=True, default=0)
    dateCreated = models.DateTimeField(auto_now_add=True)


class Order(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="user_order", null=True)
    paymentMethod = models.CharField(max_length=200, null=True, blank=True)
    dateCreated = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.dateCreated)

models.py
class OrderItem(models.Model):
    product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
    image = models.CharField(max_length=200, null=True, blank=True)
    order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
    name = models.CharField(max_length=200, null=True, blank=True)
    qty = models.IntegerField(null=True, blank=True, default=0)
    price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)

    def __str__(self):
        return str(self.name)

The view I', trying to test is shown below:

def addOrderItems(request):
    user = request.user
    data = request.data

    orderItems = data['orderItems']

    if orderItems and len(orderItems) == 0:
        return Response({'detail': 'Order item was not provided'}, status=status.HTTP_400_BAD_REQUEST)
    else:

        # Step 1: Create order

        order = Order.objects.create(
            user=user,
            paymentMethod=data['paymentMethod'],
        )

        #Step 2: Create shipping address

        shipping = ShippingAddress.objects.create(
            order=order,
            address=data['shippingAddress']['address'],
        )

        # Step 3: Create order items, then set order to orderItem relationship
        for i in orderItems:
            product = Product.objects.get(id=i['product'])

            item = OrderItem.objects.create(
                product=product,
                order=order,
                name=product.name,
                qty=i['qty'],
                price=i['price'],
                image=product.image.url,
            )

            # Step 4: Update stock
            product.countInStock -= item.qty
            product.save()

        serializer = OrderSerializer(order, many=False)
        return Response(serializer.data)

Any hint on this is well appreciated

Upvotes: 0

Views: 360

Answers (2)

Metalgear
Metalgear

Reputation: 3457

I think the payload is not correct. Now the whole product data is uploaded, but it should be changed into the product_id. Because in the view, it is being considered as the id field like Product.objects.get(id=i['product']).

So the solution is like the following. First, the payload should be changed.

{
    "orderItems":[{
        "product_id": 3,  // here I changed 
        "qty":"2",
        "price":"200"
    }],
    "shippingAddress": {
        "address":"abc", "city":"B", "postalCode":"12345",
    },
    
    "paymentMethod":"monify",
    "itemPrice":"100"
}

And in the addOrderItems function,

def addOrderItems(request):
    ...
    if orderItems and len(orderItems) == 0:
        ...
    else:
        ...
        
        # Step 3: Create order items, then set order to orderItem relationship
        for i in orderItems:
            product = Product.objects.get(id=i['product_id'])
            ...

Upvotes: 1

Nicolas Perez
Nicolas Perez

Reputation: 354

Your JSON is invalid (the "image" key in "orderItems" was messed up). To check this, you can use an online tool like https://jsonformatter.curiousconcept.com/.

Moreover, from the way you are iterating over it, orderItems should contain a list.

Here is the corrected JSON:

{
    "orderItems": [{
        "product": {"name":"abc", "brand":"def", "image": "www.example.com/img", "description":"xyz", "price":"50"},
        "qty":"2",
        "price":"200"
    }],
    "shippingAddress": {
        "address":"abc", "city":"B", "postalCode":"12345",
    },
    
    "paymentMethod":"monify",
    "itemPrice":"100"
}

In your Product model, you should also add an ID as PrimaryKey to be able to retrieve it later in your view:

import uuid

class Product(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # ... (all your other attributes)

The JSON will then include this product ID:

{
    "orderItems": [{
        "product": {"id": "9b84820b-1b4f-43a2-b576-14c3e1635906", "name":"abc", "brand":"def", "image": "www.example.com/img", "description":"xyz", "price":"50"},
        "qty":"2",
        "price":"200"
    }],
    "shippingAddress": {
        "address":"abc", "city":"B", "postalCode":"12345",
    },
    
    "paymentMethod":"monify",
    "itemPrice":"100"
}

Then the line product = Product.objects.get(id=i['product']) should be replaced with:

product = Product.objects.get(id=i['product']['id'])

Upvotes: 0

Related Questions