Updating Cart with Options

Hello everyone,

hoping to get a little direction here. i am trying to update the quantity of a cart item. the issue is that i am trying to use the item, cart session and options created from a form.

I can get the initial cart to add, and i can add various options and styles. However i cannot update the cart, i.e remove. the reason is the item 1 exists as style 1, size 1 and style 2, size 2. “Error - too many values to unpack (expected 2)”

I would like to be able to update the cart based on the unique key of either cart item or cart item id, unique would be item.name-size-style.

the closest i have been able to get is with last, but of course it removes the last item added, i know this is not the correct way.

let me know if you need more info.
thanks

def cart_remove(request, item_id):

    cart_item_id = CartItem.objects.all().last().cart_item_id

    print(cart_item_id)

    cart = Cart.objects.get(cart_id=_cart_id(request))

    item = get_object_or_404(Item, id=item_id)

    cart_item = CartItem.objects.get(

        cart=cart, item=item, cart_item_id=cart_item_id)

    if cart_item.quantity > 1:

        cart_item.quantity -= 1

        cart_item.save()

(Note: when you post code here, please enclose it between lines consisting of only three backtick (`) characters. In other words, one line that is just ```, your code, and then another line like the first.)

I think I would need to see your Cart and CartItem models, as well as the actual traceback error message you’re getting - and possibly the relevant contents of those tables as well.

hello,

sorry about the code post, will try again. I should be able to reference the CartItem id and just just update that item, not when the item id is the same.

thanks for the help, hope i added the code correctly.

I added a photo of the items in the cart.

thanks

CartItem Model is

class CartItem(models.Model):

    item = models.ForeignKey(Item, on_delete=models.CASCADE)

    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)

    quantity = models.IntegerField()

    active = models.BooleanField(default=True)

    size = models.CharField(max_length=250, blank=True, null=True)

    style = models.CharField(max_length=250, blank=True, null=True)

    cart_item_id = models.CharField(max_length=250, blank=True, null=True)

    class Meta:

        db_table = 'CartItem'

    def sub_total(self):

        return self.item.price * self.quantity

    def __str__(self):

        return str(self.item)

Cart Model:

class Cart(models.Model):

    cart_id = models.CharField(max_length=250, blank=True)

    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:

        db_table = 'Cart'

        ordering = ['date_added']

    def __str__(self):

        return self.cart_id
1 Like

Quick comment on the code formatting - you used the apostrophe (“single-quote”, “tick mark”) instead of the back tick. On the standard US keyboard it’s the key to the left of the 1 key (or the unshifted tilde ~). (Don’t know where it would be on a different keyboard.) If you edit your post and replace the three ’ with three `, you’ll see the difference.)

Ok, so trying to make sure I understand what’s going on here, a Cart is a shopping cart. It’s basically a container for CartItems.

The CartItem is an individual item being purchased in some quantity.

Given that you’ve got the implicit id field in the CartItem model, what’s the purpose or function of the cart_item_id? Where and how is it set?

It looks like the purpose of the cart_remove function is to reduce the number in the quantity by 1. If it’s 1, nothing happens.

I’m also going to guess that this is being done in some type of AJAX call, and not a form submittal. That implies to me that you need to be tracking this person’s cart in their session. (Otherwise, how can you tell the content of two different people’s carts apart? You’re not tracking the User in Cart, and you haven’t shown what this _cart_id function does to get the Cart from the request.

So working under the assumption that you’re tracking this individual’s Cart from their session, and that the item_id being passed into the view is the primary key from the Item table, your query for the CartItem is going to be something like this:

cart_item = CartItem.objects.get(cart_id=_cart_id(request), item=item_id)

If a person can have multiple items in the cart with the same item_id but different size or styles, then you would need to account for that with the data being submitted in the request. (You seem to be doing a lot of stuff in your cart_remove view that isn’t necessary - I’m guessing there’s more going on here than what you’ve posted, but I’m not sure what all is intended here.

Ken

Hello Ken,

I really appreciate the response. there is a lot going on there as I am trying to figure this out. Yes your one line of code makes sense over three lines. here is the add cart quantity code that works.

def cart_update(request, item_id):
    cart = Cart.objects.get(cart_id=_cart_id(request))
    item = get_object_or_404(Item, id=item_id)
    cart_item = CartItem.objects.get(
        cart=cart, item=item)

    if cart_item.quantity < cart_item.item.stock:
        cart_item.quantity += 1
        cart_item.save()
    else:
        pass
    return redirect('cart_detail')

in the cart view i have + - or delete. they all worked until i added a modelform for Options, style and size.

the initial add to cart works for all variations, i just cannot figure out how to update what is already in the cart. I need it to see the unique combination of what is in the cart including style and size, and this I do not know how to do… with the above code i get get() returned more than one CartItem – it returned 2!, which makes sense because I have two of item 1. if only one combination of size, item, and style is in the cart it works fine.

Does that make sense?

appreciate the help.

I will continue to read the docs. been a great learning experience.

sorry your code will not work as i have this to create a cart id.

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

thanks

So yes, it looks like you’re going to need to include the size and style in your request for updating the cart.

Or, the other option (which is what I would recommend) would be to include the CartItem primary key as the parameter. If that’s what’s returned in the request, then you don’t need to identify separate selections for the size & style.

Ken, that is correct. I can’t figure out how to call the cart item pk correctly. I know it exists, just can’t.

Thanks

Basically, somehow you’re issuing a request that has what you’re referring to as the item_id. What you’re going to want to do is have it submit the CartItem.id instead.

So let’s see what’s happening on the client side. What is the template being used look like? What JavaScript is involved in making the request? How is the page being built?

appreciate the help.

first get or create the session. then add the item and qty as 1 to the cart, with style and size coming from the model form. once in the cart_detail i have the update, remove single item and delete. all works without the size and style.

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


def add_cart(request, item_id):
    item = get_object_or_404(Item, id=item_id)
    try:
        cart = Cart.objects.get(cart_id=_cart_id(request))

    except Cart.DoesNotExist:
        cart = Cart.objects.create(
            cart_id=_cart_id(request)
        )
        cart.save()

    try:
        form = OptionForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            cart_item = CartItem.objects.get(item=item, cart=cart,
                                             style=cd['style'],
                                             size=cd['size'])

            if cart_item.quantity < cart_item.item.stock:
                cart_item.quantity += 1
            cart_item.save()

    except CartItem.DoesNotExist:
        form = OptionForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            CartItem.objects.create(item=item, quantity=1, cart=cart,
                                    style=cd['style'],
                                    size=cd['size'], cart_item_id=item.name+'-'+cd['style']+'-'+cd['size'])

    return redirect('cart_detail')

def cart_remove(request, item_id):
    cart = Cart.objects.get(cart_id=_cart_id(request))
    item = get_object_or_404(CartItem, id=item_id)
    cart_item = CartItem.objects.get(cart=cart, item=item)

    if cart_item.quantity > 1:
        cart_item.quantity -= 1
        cart_item.save()
    else:
        cart_item.delete()
    return redirect('cart_detail')


def cart_update(request, item_id):
    cart = Cart.objects.get(cart_id=_cart_id(request))
    item = get_object_or_404(Item, id=item_id)
    cart_item = CartItem.objects.get(
        cart=cart, item=item)

    if cart_item.quantity < cart_item.item.stock:
        cart_item.quantity += 1
        cart_item.save()
    else:
        pass
    return redirect('cart_detail')


def cart_remove_item(request, item_id):
    cart = Cart.objects.get(cart_id=_cart_id(request))
    item = get_object_or_404(Item, id=item_id)
    cart_item = CartItem.objects.get(
        item=item, cart=cart)
    cart_item.delete()
    return redirect('cart_detail')

HTML to display and buttons to trigger increase, decrease and delete

        <table class="table cart_table">
            <thread class="cart_thread">
                <tr>
                    <th colspan="4">
                        Your Items
                    </th>
                </tr>
            </thread>
            {% for cart_item in cart_items %}
            <tr>
                <td>
                    <a href="cart_item.item.get_url">
                        <img src="{{cart_item.item.image.url}}" width="100" height="100" alt="">
                    </a>
                </td>
                <td class="text-left">
                    {{ cart_item.item.name }}
                    <br>
                    SKU: {{ cart_item.item.id }}
                    <br>
                    Unit Price: ${{ cart_item.item.price }}
                    <br>
                    Qty: {{ cart_item.quantity }} x ${{ cart_item.item.price }}
                    <br>
                    Size: {{ cart_item.size }}
                    <br>
                    Style: {{ cart_item.style }}
                    <br>
                    Comment: {{ cart_item.comment }}
                </td>
                <td>
                    ${{ cart_item.sub_total }}
                </td>
                {% if cart_item.quantity < cart_item.item.stock %}
                <td>
                    &nbsp;<a href="{% url 'cart_update' cart_item.item.id %}"><i class="fas fa-plus cart_icon"></i></a>
                    &nbsp;<a href="{% url 'cart_remove' cart_item.item.id %}"><i class="fas fa-minus cart_icon"></i></a>
                    &nbsp;<a href="{% url 'cart_remove_item' cart_item.item.id %}"><i
                            class="fas fa-trash-alt cart_icon"></i></a>
                </td>
                {% else %}
                <td>
                    &nbsp;<a href="{% url 'cart_remove' cart_item.item.id %}"><i class="fas fa-minus cart_icon"></i></a>
                    &nbsp;<a href="{% url 'cart_remove_item' cart_item.item.id %}"><i
                            class="fas fa-trash-alt cart_icon"></i></a>
                </td>
                {% endif %}
            </tr>
            {% endfor %}
        </table>

thanks

In your url references for cart_update, cart_remove, etc, you can use cart_item.id. That’s the primary key for the CartItem model.

Ken,

appreciate the help, was able to get that to work.

now i am on to my next issue haha. this is fun.

I have stock tied to the item, so if i set stock to 10, add sixe of item 1 with different styles and sizes it goes through. so need to make some adjustments there.

really appreciate you taking the time to help.