Django cookie 与 session

以下是用中文详细讲解 Django 中的 CookieSession 机制,聚焦于它们的定义、用法、与用户认证(Auth)的集成,以及在多表模型(如之前的 CategoryItemTag)场景下的应用。我会保持简洁清晰,适合初学者和进阶用户,关键代码将包裹在 ““`标签中以生成 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
“`

  1. 访问:
  • 商品列表: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)、特定功能的深入讲解,或调试相关问题的方法,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注