1

As in title Django does not increase quantity of items which are already in the cart. I would be pleased for any advice. Why it happens and how can i solve this problem.

models.py

 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 OrderItem(models.Model):
    order_item = models.ForeignKey(Item, on_delete=CASCADE, null=True)
    quantity = models.IntegerField(default=1)

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

views.py

def post(self, request, pk):
    if 'buy' in request.POST:
        item = get_object_or_404(Item, id=pk)
        item_quantity = request.POST.get('quantity')
        orderItem, created = OrderItem.objects.get_or_create(order_item=item)
        cart, created = Cart.objects.get_or_create(order_user=request.user)
        cart.save()
        if Cart.objects.filter(order_items=orderItem).exists():
            orderItem.quantity += 1
        cart.order_items.add(orderItem)
        cart.save()
        return HttpResponse('Items added to the database')

   
kamilyrb
  • 2,502
  • 3
  • 10
  • 25
Kay
  • 591
  • 1
  • 7
  • 27
  • 1
    Shouldn't your `OrderItem` have a `ForeignKey` to the `Cart` it is working with, instead of a `ManyToManyField` from `Cart` to `OrderItem`. – Willem Van Onsem Sep 04 '21 at 18:27

1 Answers1

0

THe modeling looks quite odd. Normally the OrderItem should be the junction model [wiki] between an Item and a Cart, so the object looks like:

class Item(Visits, models.Model):
    # ⋮

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

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

Then we can obtain the current active Cart or create one if there is no such Cart record:

cart, __ = Cart.objects.get_or_create(
    user=request.user,
    ordered=False
)

and we can add something to that cart with:

from django.db.models import F

orderitem, created = OrderItem.objects.get_or_create(
    cart=cart,
    item_id=pk
)
if not created:
    orderitem.quantity = F('quantity') + 1
    orderitem.save(force_update=True, update_fields=['quantity'])

The view thus looks like:

def post(self, request, pk):
    if 'buy' in request.POST:
        cart, __ = Cart.objects.get_or_create(
            user=request.user,
            ordered=False
        )
        orderitem, created = OrderItem.objects.get_or_create(
            cart=cart,
            item_id=pk
        )
        if not created:
            orderitem.quantity = F('quantity') + 1
            orderitem.save(force_update=True, update_fields=['quantity'])
        return redirect('name-of-some-view')

or if the quantity can be raised with a parameter:

def post(self, request, pk):
    if 'buy' in request.POST:
        cart, __ = Cart.objects.get_or_create(
            user=request.user,
            ordered=False
        )
        quantity = request.POST.get('quantity')
        orderitem, created = OrderItem.objects.get_or_create(
            cart=cart,
            item_id=pk,
            defaults={'quantity': quantity}
        )
        if not created:
            orderitem.quantity = F('quantity') + quantity
            orderitem.save(force_update=True, update_fields=['quantity'])
        return redirect('name-of-some-view')

Note: In case of a successful POST request, you should make a redirect [Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Hello Sir, great answer but after "migrate" i get this error. ValueError: Cannot alter field shop.Cart.order_items into shop.Cart.order_items - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields) – Kay Sep 04 '21 at 22:11
  • A simple solution is to delete all the migration files except for __init__.py and delete pycache and database. Make migrations again. This will solve ValueError. For alternate answers visit https://stackoverflow.com/questions/26927705/django-migration-error-you-cannot-alter-to-or-from-m2m-fields-or-add-or-remove – Naman Jain Sep 04 '21 at 22:15