Django total price of OrderItems in Cart

Viewed 493

How can I get "total" price of items of OrderItem in cart model from these models down below? I tried doing something in views but I get attribute error that QuerySet object has no attribute 'total'

# views.py

def cart(request):
    cart = Cart.objects.filter(order_user=request.user)
    order_items = OrderItem.objects.filter(cart__in=cart)
    total = 0
    for i in order_items:
        total = i.quantity * i.item.price + cart.total
        cart.update(total=total)

# models.py

class OrderItem(models.Model):
    cart = models.ForeignKey('Cart', on_delete=CASCADE, null=True)
    item = models.ForeignKey(Item, on_delete=CASCADE, null=True)
    quantity = models.IntegerField(default=1)

class Item(Visits, models.Model):
    title = models.CharField(max_length=150)
    price =  models.IntegerField(default=1000)
    image = models.ImageField(upload_to='pictures', default='static/images/man.png')
    description = models.TextField(default="Item")
    visits = models.IntegerField(default=0)



class Cart(models.Model):
    order_user = models.OneToOneField(User, on_delete=CASCADE)
    ordered = models.BooleanField(default=False)
    total = models.IntegerField(default=0, help_text="100 = 1EUR")
    order_items = models.ManyToManyField(Item, related_name='carts', through=OrderItem )
3 Answers

You can get the total through .annotate(…) [Django-doc] and thus calculate this at the database side:

from django.db.models import F, Sum

def cart(request):
    cart = Cart.objects.annotate(
        price=Sum(F('orderitem__item__price') * F('orderitem__quantity'))
    ).get(
        order_user=request.user
    )
    cart.total = cart.price
    cart.save()
    # …

The cart object that arises from this will have an extra attribute .price that contains the price of the items multiplied with the corresponding quantity.

But this also specifies why you should not keep a total field in your Cart: you can calculate that when necessary.

The problem is coming from the first line inside the for loop where you've written:

total = i.quantity * i.item.price + cart.total

Here, the cart is a QuerySet since you've used filter when you retrieved the cart object from Cart model in the first line of cart method. filter returns QuerySet, not object. You can use get to get the cart object since there is a OneToOne relationship between the Cart and User models. Try this in the first line of your cart method:

cart = Cart.objects.get(order_user=request.user)

You also need to use save method instead of update when you want to update an object. update method belongs to QuerySet only.

class OrderItem(DateTimeModel):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order_items")
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(default=1, blank=True, null=True)
    price = models.DecimalField(max_length=100, default=0, max_digits=7, decimal_places=2)
    total = models.DecimalField(max_digits=7, decimal_places=2, default=0)
# need to add tax percentage along with it tax may be changed in future so we need to add tax percentage in this

def __str__(self):
    return f"{self.id} - {self.order}"

def get_total(self):
    return round(float(self.price) * float(self.quantity))

def save(self, *args, **kwargs):
    self.total = self.get_total()
    super().save(*args, **kwargs)
Related