Django Ecommerce View problem with incrementing a quantity up or down on products with same Product Id but different Option ID values

My issue is probably something simple but Ive been stuck on it. I am attempting to make my ecommerce shopping cart increment quantity up and down while deleting the instance when quantity gets to zero. The issue is that when you add options to a product that is in another cart_item instance with the same base Product_id its throwing an error.

Im aware it is pulling different instances of the same product_id into the same get request but im not sure how to resolve it.

MultipleObjectsReturned at /cart/add_cart/2/ get() returned more than one CartItem -- it returned 2! Request Method: GET Request URL: http://127.0.0.1:8000/cart/add_cart/2/ Django Version: 3.1 Exception Type: MultipleObjectsReturned Exception Value: get() returned more than one CartItem -- it returned 2! Exception Location: C:\Users\dave\Desktop\tnaecommerce\venv\Lib\site-packages\django\db\models\query.py, line 433, in get Python Executable: C:\Users\dave\Desktop\tnaecommerce\venv\Scripts\python.exe Python Version: 3.11.0 Python Path: ['C:\\Users\\dave\\Desktop\\tnaecommerce\\ecomwebsite\\tnaecom', 'C:\\Python311\\python311.zip', 'C:\\Python311\\DLLs', 'C:\\Python311\\Lib', 'C:\\Python311', 'C:\\Users\\dave\\Desktop\\tnaecommerce\\venv', 'C:\\Users\\dave\\Desktop\\tnaecommerce\\venv\\Lib\\site-packages'] Server time: Mon, 19 Dec 2022 16:19:11 +0000

views.py

`

from django.shortcuts import render, redirect, get_object_or_404
from .models import Cart, CartItem
from store.models import Product, ProductOption
from django.core.exceptions import ObjectDoesNotExist
from decimal import Decimal
from django.http import HttpResponse
from decimal import Decimal
from django.views import View
from django.views.generic.edit import DeleteView


def _cart_id(request):
    cart = request.session.session_key
    if not cart:
        cart = request.session.create()
    return cart

def add_cart(request, product_id, quantity=1):
  product = Product.objects.get(id = product_id) # get the product  
  product_options= request.POST.getlist('option')#gets selected product options   
  
  #
  try:
    cart = Cart.objects.get(cart_id = _cart_id(request) ) # gets cart using private function above
  except Cart.DoesNotExist:# creates a new cart instance if it doesnt exist
    Cart.objects.create(
      cart_id = _cart_id(request)
    )
  cart.save()#saves new cart if needed to be created 
  
  try: 
    cart_item = CartItem.objects.get(product = product, cart = cart)
    # if the product has multiple options, check if any of the options in the cart item matches the selected options 
    if len(product_options) > 0:
      match = False
      for option in cart_item.options.all():
        if option in product_options:
          match = True
          # adding to attempt to fix quantity increment up +1
          quantity += quantity
          cart_item.save()
      # if none of the cart item options matches the selected options, add new cart item with selected options 
      if not match:
        cart_item = CartItem.objects.create(
          product = product,
          cart = cart,
          quantity = quantity,
        )
        if len(product_options) > 0:
          for item in product_options:
            cart_item.options.add(item)
        cart_item.save() 
    else:
      cart_item.quantity += quantity # adds the specified quantity of a single instance of product cart item
      cart_item.save()  
  except CartItem.DoesNotExist:
    cart_item = CartItem.objects.create(
      product = product,
      cart = cart,
      quantity = quantity
    )
    if len(product_options) > 0:
      for item in product_options:
        cart_item.options.add(item)
    cart_item.save() 
  return redirect("cart")



def remove_cart(request, product_id):
  cart = Cart.objects.get(cart_id= _cart_id(request))
  product = get_object_or_404(Product, id = product_id)
  cart_item = CartItem.objects.get(product=product, cart=cart)

  if cart_item.quantity > 1:
            cart_item.quantity -= 1
            cart_item.save()
  else:
      cart_item.delete()

  return redirect('cart')


def remove_cart_item(request, id):    
  cart_item = CartItem.objects.get(id=id) # get the cart item
  # get the cart
  try:
    cart = Cart.objects.get(cart_id = _cart_id(request) ) # gets cart using private function above
  except Cart.DoesNotExist:# creates a new cart instance if it doesnt exist
    Cart.objects.create(
      cart_id = _cart_id(request)
    )
  cart.save()#saves new cart if needed to be created     
  if cart_item.quantity > 1:
            cart_item.quantity -= 1
            cart_item.save()
  else:
      cart_item.delete() # save the cart item if there are still items left in the cart 
  return redirect("cart")



def cart(request, total = 0,total_savings=0, quantity=0, cart_items= None):
  try:
    cart = Cart.objects.get(cart_id = _cart_id(request))    
    cart_items = CartItem.objects.filter(cart=cart, is_active=True)
    for cart_item in cart_items:    
      total += cart_item.subtotal
      total_savings += cart_item.savings
      quantity = cart_item.quantity

  except ObjectDoesNotExist:
    pass

  context = {
    'total': total,    
    'quantity': quantity,
    'total_savings': total_savings,    
    'cart_items': cart_items,    
  }

  return render (request, 'store/cart.html', context)

`

url.py

`

from django.urls import path
from . import views

'''
  /cart/ starting url from root url

  '''


urlpatterns = [
  
    path('', views.cart, name='cart'),
    path('add_cart/<int:product_id>/', views.add_cart, name = 'add_cart'),
    path('remove_cart/<int:product_id>/', views.remove_cart, name = 'remove_cart'),
    #path('remove_cart_item/<int:product_id>/', views.remove_cart_item, name = 'remove_cart_item'),
    path('remove_cart_item/<int:id>/', views.remove_cart_item, name = 'remove_cart_item')
] 

`

models.py

`

from django.db import models
from store.models import *
from decimal import Decimal

# Create your models here.

class CouponCode(models.Model):
  """Model definition for CouponCode."""
  name = models.CharField("Coupon Code Name", max_length=50)
  code = models.CharField("Verbatim Coupon Code", max_length=50)
  discount = models.DecimalField("Discount as decimal of 100", max_digits=3, decimal_places=2, help_text="For a 20 percent discount enter .80")
  usagecount = models.IntegerField(default=0)
  products = models.ManyToManyField(Product)
  is_active = models.BooleanField(default=False)
  

  # TODO: Define fields here

  class Meta:
    """Meta definition for CouponCode."""

    verbose_name = 'CouponCode'
    verbose_name_plural = 'CouponCodes'

  def __str__(self):
    """Unicode representation of CouponCode."""
    return f"Coupon: {self.name}, Code: {self.discount}, Active: {self.is_active}"

class Cart(models.Model):
  """Model definition for Cart."""
  cart_id = models.CharField(max_length= 250, blank = True)
  date_added = models.DateField("Date Added", auto_now_add=True)
  # TODO: Define fields here

  class Meta:
    """Meta definition for Cart."""

    verbose_name = 'Cart'
    verbose_name_plural = 'Carts'

  def __str__(self):
    """Unicode representation of Cart."""
    return self.cart_id


class CartItem(models.Model):
  """Still need to write loop function adding product option cost to 
  base product price."""
  product = models.ForeignKey(Product, on_delete = models.CASCADE)
  options = models.ManyToManyField( ProductOption, blank=True)
  cart = models.ForeignKey(Cart, on_delete = models.CASCADE, null=True)
  quantity = models.IntegerField(default=1)
  is_active = models.BooleanField(default = True)
  date_added = models.DateTimeField("Date/Time added", auto_now=False, auto_now_add=True)
  # TODO: Define fields here     
  
  @property # 
  def subtotal(self):
    option_subtotal=0
    for item in self.options.all():      
      option_subtotal += item.price  
    original_price = self.product.product_price + option_subtotal#- self.couponcode
    quantity = self.quantity  
    price = original_price    
    quantity = self.quantity
    if quantity >= 10:
      price = round(price * Decimal(.80),2)
    elif quantity >= 8:
      price = round(price * Decimal(.85),2)
    elif quantity >= 6:
      price = round(price * Decimal(.90),2)
    elif quantity >= 4:
      price = round(price * Decimal(.93),2)
    elif quantity >= 2:
      price = round(price * Decimal(.95),2)      
    else: 
      price = round(price, 2)
    # rounding price to 2 decimal places
    price = price  
    subtotal = price * quantity
    return subtotal

  @property # 
  def savings(self):
    option_subtotal=0   
    quantity = self.quantity
    for item in self.options.all():      
      option_subtotal += item.price  
    original_price = self.product.product_price + option_subtotal 
    original_price *= quantity   
    price = original_price 
    '''Coupon code discount '''    
    quantity = self.quantity
    if quantity >=10:
      price = round(price * Decimal(.80),2)
    elif quantity >=8:
      price = round(price * Decimal(.85),2)
    elif quantity >=6:
      price = round(price * Decimal(.90),2)
    elif quantity >=4:
      price = round(price * Decimal(.93),2)
    elif quantity >=2:
      price = round(price * Decimal(.95),2)      
    else: 
      price = round(price,2)
    # rounding price to 2 decimal places
    savings = original_price - price 
    return savings

  @property
  def orig_price(self):
    option_subtotal=0   
    quantity = self.quantity

    for item in self.options.all():      
      option_subtotal += item.price  
    original_price = self.product.product_price + option_subtotal    
    original_price = original_price * quantity

    return original_price
    



  class Meta:
    """ Meta definition for CartItem """

    verbose_name = 'CartItem'
    verbose_name_plural = 'CartItems'   

  
  def __str__(self):
    
    """ Unicode representation of CartItem """

    return f"CartItem ID: {self.id}, Product: {self.product}, Quantity: {self.quantity},Cart: {self.cart.id},"

  

`

sorry to bother ya'll about something that i am quite sure is so minor. Id appreciate the help.

To resolve it I have tried any multitude of variations of pulling the cart_item id directly and have had some success in getting it to increment down just fine. I just cant get the numbers to go up and add more instances.

Im sure the solution is some variation of how I incremented the quantity value down in the

remove_cart_item

`

def remove_cart_item(request, id):    

  cart_item = CartItem.objects.get(id=id) # get the cart item
  # get the cart
  try:
    cart = Cart.objects.get(cart_id = _cart_id(request) ) # gets cart using private function above
  except Cart.DoesNotExist:# creates a new cart instance if it doesnt exist
    Cart.objects.create(
      cart_id = _cart_id(request)
    )
  cart.save()#saves new cart if needed to be created     
  if cart_item.quantity > 1:
            cart_item.quantity -= 1
            cart_item.save()
  else:
      cart_item.delete() # save the cart item if there are still items left in the cart 
  return redirect("cart")

`

Im just not sure how to implement it into the add_cart functionality with everything else that is going on up there. The error only comes up when you attempt to add another item that has included options. A base level option works fine

use filter() instead of get() when fetching cart-items in add-cart function. cart_item = CartItem.objects.filter(product = product, cart = cart).

get() returns the model object where filter returns the instance. You have one cart with many cart-items in it. So Inorder to get it, you have to use filter(). You are facing error because of that because cart-items receiving more data when get() method only return one object. The error itself says that MultipleObjectsReturned at /cart/add_cart/2/ get() returned more than one CartItem -- it returned 2!. so use filter() method

Back to Top