[ The first picture shows the add to cart button which when I click on, the product is added and the cart modal should show up with the added product. The second picture shows what I am expecting to see, but I only see this after refreshing the page. The third picture shows what I am actually seeing when I click on the add to cart button, just an empty cart modal pops up. . 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. What can I try next? 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.method == 'POST': if request.path == '/cart/update_quantity/': return self.update_quantity(request, *args, **kwargs) else: # Handle other POST requests here pass elif request.method == 'GET': if request.path == '/cart/data/': logger.debug(f"Request Path: {request.path}") return self.get_cart_data(request) else: # Handle other GET requests here pass # Fall back to the default behavior if the request doesn't match any of the above conditions 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='modal-cart-block'> <div class='modal-cart-main flex'> <div class="right cart-block md:w-1/2 w-full py-6 relative overflow-hidden"> <div class="heading px-6 pb-3 flex items-center justify-between relative"> <div class="heading5">Shopping Cart</div> <div class="close-btn absolute right-6 top-0 w-6 h-6 rounded-full bg-surface flex items-center justify-center duration-300 cursor-pointer hover:bg-black hover:text-white"> <i class="ph ph-x text-sm"></i> </div> </div> <div class="time countdown-cart px-6"> <div class=" flex items-center gap-3 px-5 py-3 bg-green rounded-lg"> <p class='text-3xl'></p> <div class="caption1">Your cart will expire in <span class='text-red caption1 font-semibold'><span class="minute">04</span>:<span class="second">59</span></span> minutes!<br /> Please checkout now before your items sell out!</div> </div> </div> <div class="heading banner mt-3 px-6"> <div class="text">Buy <span class="text-button"> $<span class="more-price">0</span>.00 </span> <span>more to get </span> <span class="text-button">freeship</span> </div> <div class="tow-bar-block mt-3"> <div class="progress-line"></div> </div> </div> <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 %} </div> <div class="footer-modal bg-white absolute bottom-0 left-0 w-full"> <div class="flex items-center justify-center lg:gap-14 gap-8 px-6 py-4 border-b border-line"> <div class="note-btn item flex items-center gap-3 cursor-pointer"> <i class="ph ph-note-pencil text-xl"></i> <div class="caption1">Note</div> </div> <div class="shipping-btn item flex items-center gap-3 cursor-pointer"> <i class="ph ph-truck text-xl"></i> <div class="caption1">Shipping</div> </div> <div class="coupon-btn item flex items-center gap-3 cursor-pointer"> <i class="ph ph-tag text-xl"></i> <div class="caption1">Coupon</div> </div> </div> <div class="flex items-center justify-between pt-6 px-6"> <div class="heading5">Subtotal</div> <div class="total-price">${{ total_price }}</div> </div> <div class="block-button text-center p-6"> <div class="flex items-center gap-4"> <a href='{% url "cart" %}' class='button-main basis-1/2 bg-white border border-black text-black text-center uppercase'> View cart </a> <a href='checkout.html' class='button-main basis-1/2 text-center uppercase'> Check Out </a> </div> <div class="text-button-uppercase continue mt-4 text-center has-line-before cursor-pointer inline-block"> Or continue shopping</div> </div> <div class='tab-item note-block'> <div class="px-6 py-4 border-b border-line"> <div class="item flex items-center gap-3 cursor-pointer"> <i class="ph ph-note-pencil text-xl"></i> <div class="caption1">Note</div> </div> </div> <div class="form pt-4 px-6"> <textarea name="form-note" id="form-note" rows=4 placeholder='Add special instructions for your order...' class='caption1 py-3 px-4 bg-surface border-line rounded-md w-full'></textarea> </div> <div class="block-button text-center pt-4 px-6 pb-6"> <div class='button-main w-full text-center'>Save</div> <div class="cancel-btn text-button-uppercase mt-4 text-center has-line-before cursor-pointer inline-block">Cancel</div> </div> </div> <div class='tab-item shipping-block'> <div class="px-6 py-4 border-b border-line"> <div class="item flex items-center gap-3 cursor-pointer"> <i class="ph ph-truck text-xl"></i> <div class="caption1">Estimate shipping rates</div> </div> </div> <div class="form pt-4 px-6"> <div class=""> <label for='select-country' class="caption1 text-secondary">Country/region</label> <div class="select-block relative mt-2"> <select id="select-country" name="select-country" class='w-full py-3 pl-5 rounded-xl bg-white border border-line'> <option value="Country/region">Country/region</option> <option value="France">France</option> <option value="Spain">Spain</option> <option value="UK">UK</option> <option value="USA">USA</option> </select> <i class="ph ph-caret-down text-xs absolute top-1/2 -translate-y-1/2 md:right-5 right-2"></i> </div> </div> <div class="mt-3"> <label for='select-state' class="caption1 text-secondary">State</label> <div class="select-block relative mt-2"> <select id="select-state" name="select-state" class='w-full py-3 pl-5 rounded-xl bg-white border border-line'> <option value="State">State</option> <option value="Paris">Paris</option> <option value="Madrid">Madrid</option> <option value="London">London</option> <option value="New York">New York</option> </select> <i class="ph ph-caret-down text-xs absolute top-1/2 -translate-y-1/2 md:right-5 right-2"></i> </div> </div> <div class="mt-3"> <label for='select-code' class="caption1 text-secondary">Postal/Zip Code</label> <input class="border-line px-5 py-3 w-full rounded-xl mt-3" id="select-code" type="text" placeholder="Postal/Zip Code" /> </div> </div> <div class="block-button text-center pt-4 px-6 pb-6"> <div class='button-main w-full text-center'>Calculator </div> <div class="cancel-btn text-button-uppercase mt-4 text-center has-line-before cursor-pointer inline-block">Cancel</div> </div> </div> <div class='tab-item coupon-block'> <div class="px-6 py-4 border-b border-line"> <div class="item flex items-center gap-3 cursor-pointer"> <i class="ph ph-tag text-xl"></i> <div class="caption1">Add A Coupon Code</div> </div> </div> <div class="form pt-4 px-6"> <div class=""> <label for='select-discount' class="caption1 text-secondary">Enter Code</label> <input class="border-line px-5 py-3 w-full rounded-xl mt-3" id="select-discount" type="text" placeholder="Discount code" /> </div> </div> <div class="block-button text-center pt-4 px-6 pb-6"> <div class='button-main w-full text-center'>Apply</div> <div class="cancel-btn text-button-uppercase mt-4 text-center has-line-before cursor-pointer inline-block">Cancel</div> </div> </div> </div> </div> </div> </div> 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.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: Object main.js:587 Updating cart modal content... main.js:590 Cart data fetched: Object main.js:623 Creating cart item element for: Object main.js:608 Error fetching cart data: TypeError: Cannot set properties of null (setting 'textContent') at main.js:605:35 These are my terminal debug statements: Received product_id: 4 Returning updated cart data as JSON response [14/May/2024 07:42:32] "POST /cart/ HTTP/1.1" 200 169 Request Path: /cart/data/ Received request to fetch cart data Returning cart data: [{'id': 71, 'product': {'id': 4, 'name': 'Marlin Knit', 'price': 199.0, 'image': '/products/RG1.jpeg'}, 'quantity': 1}] Continue reading...