如何在Django中使用fetch()实现无刷新购物车操作
Django电商无刷新购物车实现方案
要实现无刷新购物车操作,核心是把后端视图的重定向改为返回JSON数据,前端通过fetch()发送异步请求并动态更新页面DOM。以下是具体修改步骤:
1. 后端视图(views.py)修改
将原有返回页面重定向的逻辑,改为返回包含购物车状态的JSON响应,同时保留购物车的核心业务逻辑:
from django.http import JsonResponse from .models import Product # 假设你的商品模型是Product def calculate_total_price(cart): """辅助函数:计算购物车总价""" total = 0 for product_id, quantity in cart.items(): product = Product.objects.get(id=product_id) total += product.price * quantity return total # 添加商品到购物车 def add_to_cart(request, product_id): if request.method == 'POST': # 从session获取购物车,不存在则初始化空字典 cart = request.session.get('cart', {}) product_key = str(product_id) # 更新商品数量 cart[product_key] = cart.get(product_key, 0) + 1 request.session['cart'] = cart # 准备返回的状态数据 total_items = sum(cart.values()) total_price = calculate_total_price(cart) return JsonResponse({ 'success': True, 'total_items': total_items, 'total_price': f"¥{total_price:.2f}", 'product_count': cart[product_key] }) return JsonResponse({'success': False}, status=400) # 从购物车删除商品 def remove_from_cart(request, product_id): if request.method == 'POST': cart = request.session.get('cart', {}) product_key = str(product_id) if product_key in cart: del cart[product_key] request.session['cart'] = cart total_items = sum(cart.values()) total_price = calculate_total_price(cart) if cart else 0 return JsonResponse({ 'success': True, 'total_items': total_items, 'total_price': f"¥{total_price:.2f}" }) return JsonResponse({'success': False, 'message': '商品不在购物车中'}, status=404) return JsonResponse({'success': False}, status=400) # 清空购物车 def clear_cart(request): if request.method == 'POST': request.session['cart'] = {} return JsonResponse({ 'success': True, 'total_items': 0, 'total_price': '¥0.00' }) return JsonResponse({'success': False}, status=400)
2. 前端JS(shopping-cart.js)修改
添加事件监听,阻止默认页面跳转/表单提交,用fetch()发送异步请求并更新页面:
// 辅助函数:获取Django需要的CSRF Token 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; } // 监听"添加到购物车"表单提交 document.querySelectorAll('.add-to-cart-form').forEach(form => { form.addEventListener('submit', async (e) => { e.preventDefault(); // 阻止页面刷新 const productId = form.dataset.productId; const url = `/cart/add/${productId}/`; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { // 更新页面上的购物车统计信息 document.getElementById('cart-total-items').textContent = data.total_items; document.getElementById('cart-total-price').textContent = data.total_price; // 可选:更新商品详情页的购物车数量显示 const countEl = document.getElementById(`product-count-${productId}`); if (countEl) countEl.textContent = data.product_count; alert('商品已添加到购物车'); } else { alert('添加失败,请重试'); } } catch (err) { console.error('请求错误:', err); alert('网络异常,请稍后重试'); } }); }); // 监听"删除商品"按钮点击 document.querySelectorAll('.remove-from-cart-btn').forEach(btn => { btn.addEventListener('click', async (e) => { e.preventDefault(); const productId = btn.dataset.productId; const url = `/cart/remove/${productId}/`; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { // 移除页面上的商品行 const productRow = btn.closest('.cart-product-row'); if (productRow) productRow.remove(); // 更新购物车统计 document.getElementById('cart-total-items').textContent = data.total_items; document.getElementById('cart-total-price').textContent = data.total_price; // 购物车为空时显示提示 if (data.total_items === 0) { document.getElementById('cart-items-container').innerHTML = '<p class="empty-cart">购物车为空</p>'; } } else { alert(data.message || '删除失败'); } } catch (err) { console.error('请求错误:', err); alert('网络异常,请稍后重试'); } }); }); // 监听"清空购物车"按钮点击 document.getElementById('clear-cart-btn').addEventListener('click', async (e) => { e.preventDefault(); if (!confirm('确定要清空购物车吗?')) return; const url = '/cart/clear/'; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { document.getElementById('cart-items-container').innerHTML = '<p class="empty-cart">购物车为空</p>'; document.getElementById('cart-total-items').textContent = data.total_items; document.getElementById('cart-total-price').textContent = data.total_price; } else { alert('清空失败'); } } catch (err) { console.error('请求错误:', err); alert('网络异常,请稍后重试'); } });
3. HTML(shopping-cart.html)调整
给需要动态更新的元素添加ID,给交互按钮/表单添加数据属性:
<!-- 购物车商品列表容器 --> <div id="cart-items-container"> {% for product_id, quantity in cart.items %} {% with product=Product.objects.get(id=product_id) %} <div class="cart-product-row"> <h4>{{ product.name }}</h4> <p>单价: ¥{{ product.price }}</p> <p>数量: {{ quantity }}</p> <!-- 删除按钮:添加data-product-id传递商品ID --> <a href="#" class="remove-from-cart-btn" data-product-id="{{ product_id }}">删除</a> </div> {% endwith %} {% empty %} <p class="empty-cart">购物车为空</p> {% endfor %} </div> <!-- 购物车统计栏 --> <div class="cart-summary"> <p>总商品数: <span id="cart-total-items">{{ total_items }}</span></p> <p>总价: <span id="cart-total-price">{{ total_price }}</span></p> <!-- 清空购物车按钮 --> <button id="clear-cart-btn">清空购物车</button> </div> <!-- 商品详情页的添加表单示例 --> <form class="add-to-cart-form" data-product-id="{{ product.id }}"> {% csrf_token %} <button type="submit">添加到购物车</button> </form>
4. 路由配置(urls.py)
确保视图对应的URL路由正确:
from django.urls import path from . import views urlpatterns = [ path('cart/', views.shopping_cart, name='shopping_cart'), path('cart/add/<int:product_id>/', views.add_to_cart, name='add_to_cart'), path('cart/remove/<int:product_id>/', views.remove_from_cart, name='remove_from_cart'), path('cart/clear/', views.clear_cart, name='clear_cart'), ]
内容的提问来源于stack exchange,提问作者Mark Renton
相关产品推荐
相关产品推荐

