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

Django页面加载前生成Cookie遇KeyError: 'device' 求解决方案

问题原因

出现KeyError: 'device'是因为后端模板标签执行时机早于前端Cookie生成。第一次请求页面时,Django先渲染模板(包括调用cart_item_count过滤器),此时浏览器还没收到并发送device Cookie,导致request.COOKIES中没有该键,触发报错。

解决方案

方案1:后端主动生成并设置Cookie(推荐)

把UUID生成逻辑移到后端,确保第一次请求就将Cookie发送给浏览器,同时模板标签能获取到值。

修改视图文件(views.py)

from django.views.generic import ListView
from .models import ShopItem
import uuid

class HomeView(ListView):
    model = ShopItem
    paginate_by = 4
    template_name = "home-page.html"

    def get(self, request, *args, **kwargs):
        # 检查请求中是否存在device Cookie
        if 'device' not in request.COOKIES:
            device_id = str(uuid.uuid4())
            # 先获取响应对象
            response = super().get(request, *args, **kwargs)
            # 设置Cookie,路径设为根目录确保全站可用
            response.set_cookie('device', device_id, path='/')
            return response
        return super().get(request, *args, **kwargs)

修改模板标签(cart_tag.py)

用get方法避免直接索引字典引发KeyError,同时处理无device的情况:

from django import template
from store.models import Order, Customer
register = template.Library()

@register.filter
def cart_item_count(request):
    try:
        customer = request.user.customer
    except AttributeError:
        # 安全获取device Cookie,不存在则返回None
        device = request.COOKIES.get('device')
        if not device:
            # 首次请求无Cookie时直接返回0,后续请求会带上Cookie
            return 0
        customer, _ = Customer.objects.get_or_create(device=device)
    qs = Order.objects.filter(customer=customer, ordered=False)
    return qs[0].items.count() if qs.exists() else 0

方案2:前端AJAX获取购物车数量

通过前端异步请求获取数量,等Cookie生成后再发起请求,避免后端渲染时依赖未存在的Cookie。

修改navbar.html

<span class="badge red z-depth-1 mr-1" id="cart-count">0</span>
<script>
// 页面加载完成后请求购物车数量
window.addEventListener('load', function() {
    let device = getCookie('device');
    fetch('/api/cart-item-count/', {
        headers: {
            'X-CSRFToken': getCookie('csrftoken'),
            'device': device
        }
    })
    .then(response => response.json())
    .then(data => {
        document.getElementById('cart-count').textContent = data.count;
    });
});
</script>

添加API视图(views.py)

from django.http import JsonResponse
from store.models import Order, Customer

def cart_item_count_api(request):
    if request.user.is_authenticated:
        customer = request.user.customer
    else:
        # 优先从请求头取device,也兼容Cookie方式
        device = request.headers.get('device') or request.COOKIES.get('device')
        if not device:
            return JsonResponse({'count': 0})
        customer, _ = Customer.objects.get_or_create(device=device)
    qs = Order.objects.filter(customer=customer, ordered=False)
    count = qs[0].items.count() if qs.exists() else 0
    return JsonResponse({'count': count})

添加URL配置(urls.py)

from django.urls import path
from .views import HomeView, cart_item_count_api

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
    path('api/cart-item-count/', cart_item_count_api, name='cart-item-count-api'),
]
额外提示
  • 方案1中,可封装一个Mixin类让所有视图继承,避免重复编写Cookie处理逻辑。
  • Cookie的domain参数在生产环境需设置为实际域名(如domain='.yourdomain.com'),本地开发可留空。
  • 方案2需确保前端getCookie函数能正确获取CSRF令牌,避免请求被拦截。

内容的提问来源于stack exchange,提问作者batreska

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:40:13