Products not being added to cart immediately

So I am trying to build a Django e-commerce website, where there is a cart modal which pops up when I try to add any product by clicking the add to cart button.

While the product is being added correctly in the backend (which I can verify by going to admin panel), the product just doesn’t show up immediately on my cart modal, something which is essential for the website to look good. Only when I am refreshing the page, the product shows up on my modal. Been stuck on this for the last 3 days, no clue what to do. Can someone please help me out here? Thanks!

My cart model:


class Cart(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='cart')
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(default=1)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.user.username} - {self.product.name}"

My views.py:


class CartView(LoginRequiredMixin, View):
    def get(self, request):
        cart_items = Cart.objects.filter(user=request.user)
        total_price = sum(item.product.price * item.quantity for item in cart_items)
        return render(request, 'business/cart.html', {'cart_items': cart_items, 'total_price': total_price})

    def post(self, request):
        try:
            data = json.loads(request.body)
            product_id = data.get('product_id')
        except json.JSONDecodeError:
            logger.error("Invalid JSON data in the request body")
            return JsonResponse({'error': 'Invalid JSON data'}, status=400)

        logger.debug(f'Received product_id: {product_id}')

        if not product_id:
            logger.error("No product_id provided in the request")
            return JsonResponse({'error': 'No product_id provided'}, status=400)

        product = get_object_or_404(Product, id=product_id)
        cart_item, created = Cart.objects.get_or_create(user=request.user, product=product)
        if not created:
            cart_item.quantity += 1
        cart_item.save()

        cart_items = Cart.objects.filter(user=request.user)
        cart_data = []
        for item in cart_items:
            cart_data.append({
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': item.product.image.url if item.product.image else None,
                },
                'quantity': item.quantity,
            })
        total_price = sum(item.product.price * item.quantity for item in cart_items)

        logger.debug("Returning updated cart data as JSON response")
        return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})

    def get_cart_data(self, request):
        logger.debug("Received request to fetch cart data")
        cart_items = Cart.objects.filter(user=request.user)
        cart_data = []
        for item in cart_items:
            cart_data.append({
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': str(item.product.image.url) if item.product.image else None,
                },
                'quantity': item.quantity,
            })
        total_price = sum(item.product.price * item.quantity for item in cart_items)
        logger.debug(f"Returning cart data: {cart_data}")
        return JsonResponse({'items': cart_data, 'subtotal': total_price})
    
    def update_quantity(self, request):
        try:
            data = json.loads(request.body)
            item_id = data.get('item_id')
            action = data.get('action')
        except json.JSONDecodeError:
            return JsonResponse({'error': 'Invalid JSON data'}, status=400)

        # Retrieve the cart item
        cart_item = get_object_or_404(Cart, id=item_id)

        if action == 'increase':
            cart_item.quantity += 1
        elif action == 'decrease':
            if cart_item.quantity > 1:
                cart_item.quantity -= 1

        cart_item.save()

        # Calculate total price and prepare cart data
        cart_items = Cart.objects.filter(user=request.user)
        cart_data = [
            {
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': item.product.image.url if item.product.image else None,
                },
                'quantity': item.quantity,
            }
            for item in cart_items
        ]
        total_price = sum(item.product.price * item.quantity for item in cart_items)

        return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})
    
    def dispatch(self, request, *args, **kwargs):
        if request.path == '/cart/data/':
            return self.get_cart_data(request)
        return super().dispatch(request, *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        if request.method == 'POST' and request.path == '/cart/update_quantity/':
            return self.update_quantity(request, *args, **kwargs)
        return super().dispatch(request, *args, **kwargs)

My urls.py:


urlpatterns = [
    path('cart/', views.CartView.as_view(), name='cart'),
    path('cart/data/', views.CartView.as_view(), name='cart_data'),
    path('cart/update_quantity/', views.CartView.as_view(), name='update_quantity'),
]

My cart modal in my base.html:

                    <div class="list-product px-6">
                        {% for item in cart_items %}
                        <div data-item="2" class="item py-5 flex items-center justify-between gap-3 border-b border-line">
                        <div class="infor flex items-center gap-3 w-full">
                            <div class="bg-img w-[100px] aspect-square flex-shrink-0 rounded-lg overflow-hidden">
                                <img style="height: 130px" src="{{ item.product.image.url }}" alt="product" class="w-full h-full">
                            </div>
                            <div class="w-full">
                                <div class="flex items-center justify-between w-full">
                                    <div class="name text-button">{{ item.product.name }}</div>
                                    <div class="remove-cart-btn remove-btn caption1 font-semibold text-red underline cursor-pointer">
                                        Remove
                                    </div>
                                </div>
                                <div class="flex items-center justify-between gap-2 mt-3 w-full">
                                    <div class="flex items-center text-secondary2 capitalize">
                                        XS/white
                                    </div>
                                    <div class="product-price text-title">${{ item.product.price }}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    {% endfor %}

My cart modal in my main.js:

// Modal Cart
const cartIcon = document.querySelector(".cart-icon");
const modalCart = document.querySelector(".modal-cart-block");
const modalCartMain = document.querySelector(".modal-cart-block .modal-cart-main");
const closeCartIcon = document.querySelector(".modal-cart-main .close-btn");
const continueCartIcon = document.querySelector(".modal-cart-main .continue");
const addCartBtns = document.querySelectorAll(".add-cart-btn");

const openModalCart = () => {
  modalCartMain.classList.add("open");
};

const closeModalCart = () => {
  modalCartMain.classList.remove("open");
};

function getCookie(name) {
  let cookieValue = null;
  if (document.cookie && document.cookie !== '') {
    const cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === (name + '=')) {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

const csrftoken = getCookie('csrftoken');


const addToCart = (productId) => {
  const product_id = productId;
  console.log('Product ID:', product_id);

  fetch('/cart/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken,
    },
    body: JSON.stringify({ product_id }),
  })
    .then(response => {
      if (!response.ok) {
        throw new Error('Failed to add product to cart');
      }
      return response.json();
    })
    .then(data => {
      if (data.success) {
        console.log('Product added successfully:', data);
        updateCartModalContent(); // Ensure this function is called immediately after adding the product
        openModalCart();
      }
    })
    .catch(error => console.error('Error:', error));
};




document.addEventListener("DOMContentLoaded", function() {
  const plusIcons = document.querySelectorAll(".ph-plus");
  const minusIcons = document.querySelectorAll(".ph-minus");
  
  plusIcons.forEach(icon => {
      icon.addEventListener("click", function() {
          const itemId = icon.dataset.itemId;
          updateQuantity(itemId, 'increase');
      });
  });
  
  minusIcons.forEach(icon => {
      icon.addEventListener("click", function() {
          const itemId = icon.dataset.itemId;
          updateQuantity(itemId, 'decrease');
      });
  });

  function updateQuantity(itemId, action) {
    fetch('/cart/update_quantity/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': csrftoken,
      },
      body: JSON.stringify({ item_id: itemId, action: action }),
    })
    .then(response => {
      if (!response.ok) {
        throw new Error('Failed to update quantity');
      }
      return response.json();
    })
    .then(data => {
      if (data.success) {
        // Update the cart display based on the response
        updateCartModalContent();
      }
    })
    .catch(error => console.error('Error:', error));
  }
});


addCartBtns.forEach((btn) => {
  btn.addEventListener('click', () => {
    const productId = btn.dataset.productId;
    console.log('Product ID from button:', productId); // Add this line
    addToCart(productId);
  });
});

cartIcon.addEventListener("click", openModalCart);
modalCart.addEventListener("click", closeModalCart);
closeCartIcon.addEventListener("click", closeModalCart);
continueCartIcon.addEventListener("click", closeModalCart);

modalCartMain.addEventListener("click", (e) => {
  e.stopPropagation();
});

function updateCartModalContent() {
  console.log('Updating cart modal content...');
  fetchCartData()
    .then((cartData) => {
      console.log('Cart data fetched:', cartData);
      const cartItemsContainer = document.querySelector('.list-product');
      cartItemsContainer.innerHTML = '';

      if (cartData.items.length === 0) {
        cartItemsContainer.innerHTML = '<p class="mt-1">No product in cart</p>';
      } else {
        cartData.items.forEach((item) => {
          const cartItem = createCartItemElement(item);
          cartItemsContainer.appendChild(cartItem);
        });
      }

      const subtotalElement = document.querySelector('.total-cart');
      const subtotal = typeof cartData.subtotal === 'number' ? cartData.subtotal : 0;
      subtotalElement.textContent = `$${subtotal.toFixed(2)}`;
    })
    .catch((error) => {
      console.error('Error fetching cart data:', error);
    });
}




function fetchCartData() {
  // Make an AJAX request to fetch the current cart data
  return fetch('/cart/data/')
    .then((response) => response.json())
    .then((data) => data);
}

function createCartItemElement(item) {
  console.log('Creating cart item element for:', item);
  const cartItemElement = document.createElement('div');
  cartItemElement.classList.add('item', 'py-5', 'flex', 'items-center', 'justify-between', 'gap-3', 'border-b', 'border-line');
  cartItemElement.dataset.item = item.id;

  const imageUrl = item.product.image || '/static/path/to/default-image.png';

  cartItemElement.innerHTML = `
    <div class="infor flex items-center gap-3 w-full">
      <div class="bg-img w-[100px] aspect-square flex-shrink-0 rounded-lg overflow-hidden">
        <img src="${imageUrl}" alt="product" class="w-full h-full">
      </div>
      <div class="w-full">
        <div class="flex items-center justify-between w-full">
          <div class="name text-button">${item.product.name}</div>
          <div class="remove-cart-btn remove-btn caption1 font-semibold text-red underline cursor-pointer">
            Remove
          </div>
        </div>
        <div class="flex items-center justify-between gap-2 mt-3 w-full">
          <div class="flex items-center text-secondary2 capitalize">
            XS/white
          </div>
          <div class="product-price text-title">$${item.product.price}</div>
        </div>
      </div>
    </div>
  `;

  return cartItemElement;
}

Note:

These are my console debug statements:

Product ID from button: 4
main.js:496 Product ID: 4
main.js:514 Product added successfully: {success: true, items: Array(1), subtotal: '597.00'}
main.js:587 Updating cart modal content...
main.js:608 Error fetching cart data: SyntaxError: Unexpected token '<', "

<!DOCTYPE "... is not valid JSON

These are my terminal debug statements:

Received product_id: 4
Returning updated cart data as JSON response

Debug statements from get_cart_data method in my cart view aren’t being printed in the terminal.

In the network tab of your browser’s developer tools, what is it showing as the response coming back from the post?

Is the urls.py fragment posted above your root urls.py file, or an app urls.py file? If an app urls.py, please post your root urls.py.

You might also want to add a print statement in your dispatch method to see what you’re seeing as request.path.

When I click add to cart button, the response from my network tab is:

phosphor-icons.js	200	script	marlin-knit/:4027	(memory cache)	0 ms	
swiper-bundle.min.js	200	script	marlin-knit/:4028	(memory cache)	0 ms	
main.js	200	script	marlin-knit/:4029	(memory cache)	0 ms	
blog.js	200	script	marlin-knit/:4030	(memory cache)	0 ms	
jquery.min.js	200	script	marlin-knit/:4031	(memory cache)	0 ms	
magnific-popup.min.js	200	script	marlin-knit/:4032	(memory cache)	0 ms	
product-detail.js	200	script	marlin-knit/:4033	(memory cache)	0 ms	
shop.js	200	script	marlin-knit/:4034	(memory cache)	0 ms	
slick.min.js	200	script	marlin-knit/:4035	(memory cache)	0 ms	
modal.js	200	script	marlin-knit/:4036	(memory cache)	0 ms						

The urls posted above are my business app urls.py. My root urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('business.urls')),
    path('', include('users.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

After adding logger.debug(f"Request Path: {request.path}") in my dispatch method, I get:

Received product_id: 4
Returning updated cart data as JSON response
[12/May/2024 00:19:36] "POST /cart/ HTTP/1.1" 200 169
Request Path: /cart/data/
Received request to fetch cart data
Returning cart data: [{'id': 48, 'product': {'id': 4, 'name': 'Marlin Knit', 'price': 199.0, 'image': '/products/RG1.jpeg'}, 'quantity': 1}]