You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:47:44