Django cookie 与 session
以下是用中文详细讲解 Django 中的 Cookie 和 Session 机制,聚焦于它们的定义、用法、与用户认证(Auth)的集成,以及在多表模型(如之前的 Category
、Item
、Tag
)场景下的应用。我会保持简洁清晰,适合初学者和进阶用户,关键代码将包裹在 ““`标签中以生成 artifact。如果你有特定问题或需要更深入的内容,请随时告知!
什么是 Cookie 和 Session?
- Cookie:
- 存储在客户端浏览器的小型数据(通常不超过 4KB)。
- 用于保存用户偏好、跟踪状态(如购物车内容)。
- Django 通过
request.COOKIES
读取,response.set_cookie()
设置。 - Session:
- 存储在服务器端的数据,通过 Cookie 中的 session ID 与客户端关联。
- 用于维护用户会话(如登录状态)。
- Django 提供内置 Session 框架,基于
django.contrib.sessions
。 - 区别:
- Cookie:客户端存储,适合轻量数据,易被篡改。
- Session:服务器存储,安全,适合敏感数据(如用户认证)。
- 与认证的集成:
- Django Auth 使用 Session 维护登录状态。
- Cookie 可存储额外信息(如用户偏好)。
1. 配置 Cookie 和 Session
确保 Django 项目正确配置以支持 Cookie 和 Session。
1.1 配置 settings.py
Session 依赖 django.contrib.sessions
,默认已启用:
INSTALLED_APPS = [
...
'django.contrib.sessions', # Session 支持
'django.contrib.auth', # 认证支持
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
]
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware', # Session 中间件
'django.contrib.auth.middleware.AuthenticationMiddleware', # 用户认证
'django.contrib.messages.middleware.MessageMiddleware',
...
]
# Session 配置(可选)
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 数据库存储(默认)
# SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 缓存存储
SESSION_COOKIE_AGE = 1209600 # Session 有效期(秒,2周默认)
SESSION_COOKIE_SECURE = True # 仅 HTTPS 传输(生产环境)
SESSION_COOKIE_HTTPONLY = True # 禁止 JS 访问 Cookie
- 说明:
SESSION_ENGINE
:支持数据库、缓存、文件等存储方式。SESSION_COOKIE_SECURE
:生产环境中启用 HTTPS。- Cookie 和 Session 自动通过
SessionMiddleware
处理。
1.2 数据库迁移
确保 Session 表存在:
python manage.py migrate
2. 模型定义(参考)
以下是之前提到的多表模型,添加 created_by
关联用户,用于与认证和 Session 结合。
示例:myapp/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name='分类名称')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
def __str__(self):
return self.name
class Meta:
db_table = 'categories'
ordering = ['name']
class Item(models.Model):
name = models.CharField(max_length=100, verbose_name='商品名称')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格')
description = models.TextField(blank=True, verbose_name='描述')
is_active = models.BooleanField(default=True, verbose_name='是否上架')
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='items',
verbose_name='分类'
)
tags = models.ManyToManyField('Tag', related_name='items', verbose_name='标签')
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name='items',
verbose_name='创建者'
)
def __str__(self):
return self.name
class Meta:
db_table = 'items'
ordering = ['-price']
class Tag(models.Model):
name = models.CharField(max_length=50, verbose_name='标签名称')
def __str__(self):
return self.name
class Meta:
db_table = 'tags'
ordering = ['name']
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name=’分类名称’)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=’创建时间’)
def __str__(self):
return self.name
class Meta:
db_table = 'categories'
ordering = ['name']
class Item(models.Model):
name = models.CharField(max_length=100, verbose_name=’商品名称’)
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=’价格’)
description = models.TextField(blank=True, verbose_name=’描述’)
is_active = models.BooleanField(default=True, verbose_name=’是否上架’)
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name=’items’,
verbose_name=’分类’
)
tags = models.ManyToManyField(‘Tag’, related_name=’items’, verbose_name=’标签’)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name=’items’,
verbose_name=’创建者’
)
def __str__(self):
return self.name
class Meta:
db_table = 'items'
ordering = ['-price']
class Tag(models.Model):
name = models.CharField(max_length=50, verbose_name=’标签名称’)
def __str__(self):
return self.name
class Meta:
db_table = 'tags'
ordering = ['name']
“`python
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name=’分类名称’)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=’创建时间’)
def __str__(self):
return self.name
class Meta:
db_table = 'categories'
ordering = ['name']
class Item(models.Model):
name = models.CharField(max_length=100, verbose_name=’商品名称’)
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=’价格’)
description = models.TextField(blank=True, verbose_name=’描述’)
is_active = models.BooleanField(default=True, verbose_name=’是否上架’)
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name=’items’,
verbose_name=’分类’
)
tags = models.ManyToManyField(‘Tag’, related_name=’items’, verbose_name=’标签’)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name=’items’,
verbose_name=’创建者’
)
def __str__(self):
return self.name
class Meta:
db_table = 'items'
ordering = ['-price']
class Tag(models.Model):
name = models.CharField(max_length=50, verbose_name=’标签名称’)
def __str__(self):
return self.name
class Meta:
db_table = 'tags'
ordering = ['name']
---
### 3. 使用 Cookie
Cookie 用于存储客户端数据,如用户偏好或临时信息。
#### 示例:设置和读取 Cookie
##### 示例:`myapp/views.py`
python
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Item
from .forms import ItemForm
def set_cookie_view(request):
response = render(request, ‘myapp/success.html’, {‘message’: ‘Cookie 已设置’})
# 设置 Cookie
response.set_cookie(
key=’preferred_category’,
value=’books’,
max_age=3600, # 1 小时
secure=True, # 仅 HTTPS
httponly=True # 禁止 JS 访问
)
return response
def read_cookie_view(request):
# 读取 Cookie
preferred_category = request.COOKIES.get(‘preferred_category’, ‘无偏好’)
items = Item.objects.filter(category__name=preferred_category)
return render(request, ‘myapp/item_list.html’, {
‘items’: items,
‘preferred_category’: preferred_category
})
@login_required
def item_create(request):
if request.method == ‘POST’:
form = ItemForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.created_by = request.user
item.save()
form.save_m2m()
return redirect(‘myapp:item_list’)
else:
form = ItemForm()
return render(request, ‘myapp/item_form.html’, {‘form’: form})
@login_required
def item_list(request):
items = Item.objects.select_related(‘category’, ‘created_by’).prefetch_related(‘tags’).filter(created_by=request.user)
return render(request, ‘myapp/item_list.html’, {‘items’: items})
- **说明**:
- `set_cookie`:设置 Cookie,指定键、值、有效期等。
- `request.COOKIES.get`:读取 Cookie,默认值防止 KeyError。
- Cookie 数据存储在客户端,适合非敏感信息。
---
### 4. 使用 Session
Session 用于存储服务器端数据,与用户认证结合紧密。
#### 示例:设置和读取 Session
##### 示例:`myapp/views.py`
python
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
def set_session_view(request):
# 设置 Session
request.session[‘recent_items’] = [1, 2, 3] # 最近浏览的商品 ID
request.session[‘last_visit’] = ‘2025-08-01’
return render(request, ‘myapp/success.html’, {‘message’: ‘Session 已设置’})
@login_required
def read_session_view(request):
# 读取 Session
recent_items = request.session.get(‘recent_items’, [])
last_visit = request.session.get(‘last_visit’, ‘未知’)
items = Item.objects.filter(id__in=recent_items, created_by=request.user)
return render(request, ‘myapp/item_list.html’, {
‘items’: items,
‘last_visit’: last_visit
})
- **说明**:
- `request.session`:字典式接口,存储服务器端数据。
- Session 数据通过 Cookie 中的 `sessionid` 关联用户。
- 自动序列化为 JSON,存储在数据库(默认)。
#### 4.1 与认证结合
Django Auth 自动使用 Session 维护登录状态。
##### 示例:记录用户浏览历史
python
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Item
@login_required
def item_detail(request, pk):
item = get_object_or_404(Item, pk=pk)
# 记录浏览历史到 Session
recent_items = request.session.get(‘recent_items’, [])
if pk not in recent_items:
recent_items.append(pk)
request.session[‘recent_items’] = recent_items[:5] # 限制最多 5 条
return render(request, ‘myapp/item_detail.html’, {‘item’: item})
- **说明**:
- 每次访问商品详情,记录 ID 到 Session。
- 限制历史记录数量,避免 Session 数据过大。
---
### 5. 模板
以下是与 Cookie 和 Session 结合的模板。
#### 示例:`myapp/templates/myapp/item_list.html`
html
{% extends ‘base.html’ %}
{% block content %}
商品列表
{% if user.is_authenticated %}
欢迎,{{ user.username }}!登出
上次访问:{{ last_visit|default:”未知” }}
偏好分类:{{ preferred_category|default:”无” }}
{% else %}
请登录
{% endif %}
设置偏好分类
设置 Session
- {{ item.name }} – ¥{{ item.price }} ({{ item.category.name }}) – 创建者: {{ item.created_by.username }}
标签: {% for tag in item.tags.all %} {{ tag.name }}{% if not forloop.last %}, {% endif %} {% empty %} 无标签 {% endfor %} - 暂无商品
{% endblock %}
#### 示例:`myapp/templates/myapp/item_detail.html`
html
{% extends ‘base.html’ %}
{% block content %}
{{ item.name }}
价格:¥{{ item.price }}
分类:{{ item.category.name }}
描述:{{ item.description|default:”无描述” }}
状态:{% if item.is_active %}上架{% else %}下架{% endif %}
创建者:{{ item.created_by.username }}
标签: {% for tag in item.tags.all %} {{ tag.name }}{% if not forloop.last %}, {% endif %} {% empty %} 无标签 {% endfor %}
返回列表
{% endblock %}
#### 示例:基础模板 `templates/base.html`
html
{% block title %}我的网站{% endblock %}
{% if user.is_authenticated %} 登出 {% else %} 登录注册 {% endif %} {% block content %} {% endblock %} © 2025 我的网站
---
### 6. URL 路由
将视图绑定到 URL。
#### 示例:`myapp/urls.py`
python
from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView
from . import views
app_name = ‘myapp’
urlpatterns = [
path(”, views.item_list, name=’item_list’),
path(‘items//’, views.item_detail, name=’item_detail’),
path(‘items/create/’, views.item_create, name=’item_create’),
path(‘register/’, views.register, name=’register’),
path(‘login/’, LoginView.as_view(template_name=’myapp/login.html’), name=’login’),
path(‘logout/’, LogoutView.as_view(next_page=’myapp:login’), name=’logout’),
path(‘set-cookie/’, views.set_cookie_view, name=’set_cookie’),
path(‘read-cookie/’, views.read_cookie_view, name=’read_cookie’),
path(‘set-session/’, views.set_session_view, name=’set_session’),
path(‘read-session/’, views.read_session_view, name=’read_session’),
]
#### 项目级:`myproject/urls.py`
python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(‘admin/’, admin.site.urls),
path(”, include(‘myapp.urls’, namespace=’myapp’)),
]
---
### 7. 最佳实践
1. **安全性**:
- Cookie 使用 `secure=True` 和 `httponly=True`:
```python
response.set_cookie('key', 'value', secure=True, httponly=True)
```
- Session 启用 `SESSION_COOKIE_SECURE`(HTTPS)。
- 避免存储敏感数据在 Cookie 中。
2. **性能优化**:
- 使用缓存存储 Session(`SESSION_ENGINE='django.contrib.sessions.backends.cache'`)。
- 限制 Session 数据量:
```python
request.session['recent_items'] = recent_items[:5]
```
3. **用户体验**:
- 使用 Cookie 保存用户偏好(如语言、主题)。
- Session 存储临时数据(如购物车、浏览历史)。
4. **调试**:
- 查看 Session 数据:
```python
print(request.session.items())
```
- 检查 Cookie:
```python
print(request.COOKIES)
```
5. **清理 Session**:
- 定期清理过期 Session:
```bash
python manage.py clearsessions
```
---
### 8. 常见问题
1. **Session 未保存**:
- 确认 `SessionMiddleware` 已启用。
- 检查 `SESSION_ENGINE` 配置。
2. **Cookie 未设置**:
- 确保 `response.set_cookie()` 返回了响应对象。
- 检查浏览器是否禁用了 Cookie。
3. **Session 数据丢失**:
- 确认 `SESSION_COOKIE_AGE` 未过期。
- 检查数据库或缓存是否正常。
4. **多表查询慢**:
- 使用 `select_related` 和 `prefetch_related`:
```python
items = Item.objects.select_related('category', 'created_by').prefetch_related('tags')
```
---
### 9. 运行项目
1. 确保迁移:
bash
python manage.py makemigrations
python manage.py migrate
2. 启动服务器:
bash
python manage.py runserver
“`
- 访问:
- 商品列表:
http://127.0.0.1:8000/
- 设置 Cookie:
http://127.0.0.1:8000/set-cookie/
- 读取 Cookie:
http://127.0.0.1:8000/read-cookie/
- 设置 Session:
http://127.0.0.1:8000/set-session/
- 读取 Session:
http://127.0.0.1:8000/read-session/
总结
Django 的 Cookie 和 Session 机制为客户端和服务器端状态管理提供了灵活方案。Cookie 适合存储轻量、非敏感数据(如用户偏好),Session 结合 Auth 组件维护登录状态和用户数据(如浏览历史)。与多表模型集成,可实现个性化功能(如显示用户创建的商品)。遵循最佳实践(如安全配置、性能优化)确保应用健壮。
如果你需要更复杂的 Cookie/Session 示例(如购物车实现、跨域 Session)、特定功能的深入讲解,或调试相关问题的方法,请告诉我!