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
相关产品推荐
相关产品推荐

