在前三篇中,我们完成了 Django 的环境搭建、模型设计、视图模板、Admin 后台以及 ORM 高级查询。本篇将带你深入 Django 的用户交互与安全机制:Form 组件、Auth 认证系统、Cookie/Session 和中间件。学完本篇,你将能够处理复杂的表单验证、实现用户注册登录、管理用户会话,并理解 Django 的请求/响应处理流程。
第一部分:Django Form 组件
1.1 为什么需要 Form 组件?
在 Web 开发中,处理表单是常见且复杂的任务。你需要:
- 渲染 HTML 表单字段
- 验证用户输入(格式、长度、唯一性等)
- 保留用户上次输入的内容
- 显示友好的错误提示
Django 的 Form 组件正是为了解决这些问题而设计的。它提供了:
- 自动生成 HTML 标签:减少模板中的重复代码
- 数据校验:内置多种验证规则
- 保留上次输入:出错时用户无需重新填写
- 错误提示:自动关联到对应字段
1.2 定义第一个 Form 类
在应用目录下创建 forms.py,定义一个简单的联系人表单:
1# blog/forms.py 2from django import forms 3 4class ContactForm(forms.Form): 5 name = forms.CharField( 6 label='您的姓名', 7 max_length=50, 8 required=True, 9 error_messages={'required': '请输入姓名'} 10 ) 11 email = forms.EmailField( 12 label='电子邮箱', 13 required=True, 14 error_messages={'required': '请输入邮箱', 'invalid': '请输入有效邮箱'} 15 ) 16 message = forms.CharField( 17 label='留言内容', 18 widget=forms.Textarea(attrs={'rows': 4, 'class': 'form-control'}), 19 max_length=500 20 ) 21
每个字段对应 HTML 表单的一个 <input> 元素。widget 参数可以控制渲染成哪种 HTML 控件以及添加 CSS 类。
1.3 在视图中使用表单
表单的典型处理流程是:GET 请求显示空表单,POST 请求验证并处理数据。
1# blog/views.py 2from django.shortcuts import render, redirect 3from .forms import ContactForm 4 5def contact(request): 6 if request.method == 'POST': 7 # 使用 POST 数据实例化表单(绑定表单) 8 form = ContactForm(request.POST) 9 10 # 验证数据 11 if form.is_valid(): 12 # 获取清洗后的数据 13 name = form.cleaned_data['name'] 14 email = form.cleaned_data['email'] 15 message = form.cleaned_data['message'] 16 17 # 处理数据(如发送邮件、保存到数据库) 18 # send_mail(...) 或 Contact.objects.create(...) 19 20 return redirect('contact_success') # 重定向到成功页面 21 else: 22 # GET 请求:创建空表单(未绑定) 23 form = ContactForm() 24 25 return render(request, 'blog/contact.html', {'form': form}) 26
关键点:
form.is_valid()执行所有字段的验证规则form.cleaned_data是验证通过后的“干净”数据字典- 未绑定的表单(无数据)和绑定的表单(有数据)使用同一个模板
1.4 在模板中渲染表单
1{# blog/templates/blog/contact.html #} 2{% extends "base.html" %} 3 4{% block content %} 5<h2>联系我们</h2> 6<form method="post"> 7 {% csrf_token %} 8 9 {# 渲染所有字段的默认样式 #} 10 {{ form.as_p }} 11 12 {# 或者手动渲染每个字段,以便更灵活控制 #} 13 {% comment %} 14 <div> 15 {{ form.name.label_tag }} 16 {{ form.name }} 17 {{ form.name.errors }} 18 </div> 19 {% endcomment %} 20 21 <button type="submit" class="btn btn-primary">提交</button> 22</form> 23{% endblock %} 24
Django 提供了多种渲染方式:as_p()(段落)、as_table()(表格)、as_ul()(列表)。错误信息会自动显示在对应字段旁边。
1.5 ModelForm:从模型生成表单
如果表单与模型字段基本一致,使用 ModelForm 可以大大减少代码:
1# blog/forms.py 2from .models import Article 3 4class ArticleForm(forms.ModelForm): 5 class Meta: 6 model = Article 7 fields = ['title', 'content', 'category', 'tags'] # 或 '__all__' 8 widgets = { 9 'content': forms.Textarea(attrs={'rows': 5}), 10 } 11 labels = { 12 'title': '文章标题', 13 'content': '正文', 14 } 15
在视图中,可以直接调用 form.save() 保存到数据库:
1def create_article(request): 2 if request.method == 'POST': 3 form = ArticleForm(request.POST) 4 if form.is_valid(): 5 form.save() # 自动创建并保存 Article 实例 6 return redirect('article_list') 7 else: 8 form = ArticleForm() 9 return render(request, 'blog/article_form.html', {'form': form}) 10
1.6 表单验证进阶
Django 支持多层次的验证:
1. 字段级验证:定义 clean_字段名() 方法
1class ContactForm(forms.Form): 2 # ... 3 4 def clean_name(self): 5 name = self.cleaned_data.get('name') 6 if 'admin' in name.lower(): 7 raise forms.ValidationError('姓名不能包含 "admin"') 8 return name 9
2. 表单级验证:定义 clean() 方法,用于跨字段验证
1def clean(self): 2 cleaned_data = super().clean() 3 name = cleaned_data.get('name') 4 email = cleaned_data.get('email') 5 6 # 示例:禁止某些用户名和邮箱组合 7 if name and email and 'test' in name and 'test.com' in email: 8 raise forms.ValidationError('测试用户不能使用测试邮箱') 9 10 return cleaned_data 11
第二部分:Django Auth 认证系统
Django 内置了一个功能完善的认证系统,位于 django.contrib.auth。它处理用户账户、组、权限和会话。
2.1 User 模型基础
Django 默认的 User 模型包含以下核心字段:
username:用户名(必填)password:密码(哈希存储)email:邮箱first_name/last_name:姓名is_active:是否激活is_staff:是否可登录 Adminis_superuser:是否超级用户date_joined:注册日期
2.2 创建用户
方式一:命令行创建超级用户
1python manage.py createsuperuser 2
方式二:编程方式创建普通用户
1from django.contrib.auth.models import User 2 3# 创建用户(密码会自动哈希) 4user = User.objects.create_user( 5 username='john', 6 email='john@example.com', 7 password='johnpassword' 8) 9user.last_name = 'Lennon' 10user.save() 11 12# 创建超级用户 13superuser = User.objects.create_superuser( 14 username='admin', 15 email='admin@example.com', 16 password='adminpass' 17) 18
重要:永远不要直接设置 password 属性,必须使用 set_password() 或 create_user()。
2.3 用户认证与登录
Django 提供了 authenticate() 和 login() 函数来处理用户认证。
1# blog/views.py 2from django.contrib.auth import authenticate, login, logout 3from django.contrib.auth.forms import UserCreationForm, AuthenticationForm 4from django.shortcuts import render, redirect 5 6def user_login(request): 7 if request.method == 'POST': 8 form = AuthenticationForm(request, data=request.POST) 9 if form.is_valid(): 10 username = form.cleaned_data.get('username') 11 password = form.cleaned_data.get('password') 12 user = authenticate(request, username=username, password=password) 13 if user is not None: 14 login(request, user) 15 return redirect('article_list') 16 else: 17 form = AuthenticationForm() 18 return render(request, 'blog/login.html', {'form': form}) 19 20def user_logout(request): 21 logout(request) 22 return redirect('article_list') 23
authenticate() 验证凭据,成功则返回 User 对象;login() 将用户 ID 存入 session。
2.4 注册新用户
可以使用 Django 内置的 UserCreationForm:
1from django.contrib.auth.forms import UserCreationForm 2 3def register(request): 4 if request.method == 'POST': 5 form = UserCreationForm(request.POST) 6 if form.is_valid(): 7 user = form.save() # 创建用户 8 login(request, user) # 自动登录 9 return redirect('article_list') 10 else: 11 form = UserCreationForm() 12 return render(request, 'blog/register.html', {'form': form}) 13
模板 register.html 与普通表单类似。
2.5 权限系统
Django 为每个模型自动创建四个权限:添加、修改、删除、查看。
检查权限:
1# 在视图或模板中 2if request.user.has_perm('blog.add_article'): 3 # 用户有添加文章的权限 4 pass 5 6# 在模板中 7{% if perms.blog.change_article %} 8 <a href="{% url 'edit_article' article.id %}">编辑</a> 9{% endif %} 10
自定义权限:在模型的 Meta 类中定义
1class Article(models.Model): 2 # ... 3 class Meta: 4 permissions = [ 5 ("can_publish_article", "可以发布文章"), 6 ("can_review_article", "可以审核文章"), 7 ] 8
2.6 在视图中限制访问
Django 提供了多种装饰器和混入类来限制访问:
1from django.contrib.auth.decorators import login_required 2from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin 3from django.views.generic import CreateView 4 5# 函数视图 6@login_required 7def my_view(request): 8 return render(request, 'blog/profile.html') 9 10# 类视图 11class ArticleCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): 12 model = Article 13 form_class = ArticleForm 14 permission_required = 'blog.add_article' 15 login_url = '/login/' # 未登录时重定向的URL 16
第三部分:Cookie 与 Session
3.1 Cookie 基础
Cookie 是存储在客户端浏览器的小段文本,用于跟踪用户状态。Django 通过 HttpResponse 对象设置和读取 cookie。
设置 Cookie:
1def set_cookie(request): 2 response = HttpResponse("Cookie 已设置") 3 response.set_cookie('username', 'john', max_age=3600) # 有效期1小时 4 return response 5
读取 Cookie:
1def get_cookie(request): 2 username = request.COOKIES.get('username', '匿名用户') 3 return HttpResponse(f"当前用户:{username}") 4
删除 Cookie:
1def delete_cookie(request): 2 response = HttpResponse("Cookie 已删除") 3 response.delete_cookie('username') 4 return response 5
3.2 Session 基础
Session 将数据存储在服务器端,客户端只保存 session ID(通过 cookie)。Django 默认使用数据库存储 session。
启用 Session:默认项目已启用,需要确保:
INSTALLED_APPS包含django.contrib.sessionsMIDDLEWARE包含SessionMiddleware
使用 Session:
1# 设置 session 2def set_session(request): 3 request.session['user_id'] = 123 4 request.session['preferences'] = {'theme': 'dark', 'language': 'zh'} 5 return HttpResponse("Session 已设置") 6 7# 读取 session 8def get_session(request): 9 user_id = request.session.get('user_id', '未登录') 10 return HttpResponse(f"用户ID:{user_id}") 11 12# 删除 session 13def delete_session(request): 14 request.session.flush() # 清空 session 数据并删除 cookie 15 return HttpResponse("Session 已清空") 16
3.3 Session 常用方法
request.session 是一个类似字典的对象,提供了丰富的方法:
1# 基础操作 2request.session['key'] = value # 设置值 3value = request.session.get('key') # 获取值 4del request.session['key'] # 删除键 5'key' in request.session # 检查存在 6 7# 高级操作 8request.session.set_expiry(300) # 设置5分钟后过期 9request.session.set_expiry(0) # 浏览器关闭时过期 10request.session.clear_expired() # 清理过期session 11 12# 测试 cookie 支持 13request.session.set_test_cookie() # 设置测试 cookie 14if request.session.test_cookie_worked(): # 检查测试 cookie 15 request.session.delete_test_cookie() # 删除测试 cookie 16
3.4 配置 Session 存储
Django 支持多种 session 后端:
数据库 session(默认):
1SESSION_ENGINE = 'django.contrib.sessions.backends.db' 2
缓存 session(需要配置缓存):
1SESSION_ENGINE = 'django.contrib.sessions.backends.cache' 2SESSION_CACHE_ALIAS = 'default' 3
文件 session:
1SESSION_ENGINE = 'django.contrib.sessions.backends.file' 2SESSION_FILE_PATH = '/path/to/sessions/dir' # 可选 3
cookie-based session(数据加密后存在 cookie):
1SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' 2
3.5 实战:购物车示例
1def add_to_cart(request, product_id): 2 # 获取当前购物车(session 中) 3 cart = request.session.get('cart', []) 4 5 # 添加商品 6 cart.append({ 7 'product_id': product_id, 8 'quantity': 1, 9 'added_at': str(timezone.now()) 10 }) 11 12 # 保存回 session 13 request.session['cart'] = cart 14 request.session.modified = True # 确保 Django 知道 session 已修改 15 16 return redirect('view_cart') 17 18def view_cart(request): 19 cart = request.session.get('cart', []) 20 return render(request, 'shop/cart.html', {'cart': cart}) 21
第四部分:Django 中间件
4.1 什么是中间件?
中间件是 Django 请求/响应处理过程中的钩子框架。它是一个轻量级的、底层的“插件”系统,用于全局修改 Django 的输入或输出。
每个中间件组件负责执行特定功能,例如:
SessionMiddleware:管理用户 sessionAuthenticationMiddleware:将用户与请求关联CsrfViewMiddleware:防止 CSRF 攻击
4.2 中间件的执行顺序
中间件的执行像一个洋葱:
- 请求阶段:按
MIDDLEWARE列表的顺序从上到下执行 - 响应阶段:按相反的顺序从下到上执行
1 请求进入 → 中间件1 (process_request) 2 → 中间件2 (process_request) 3 → 视图函数 4 ← 中间件2 (process_response) 5 ← 中间件1 (process_response) 6 ← 响应返回 7
4.3 自定义中间件
Django 支持函数式和类式两种中间件写法。
函数式中间件:
1# blog/middleware.py 2def simple_middleware(get_response): 3 # 初始化代码(服务器启动时执行一次) 4 5 def middleware(request): 6 # 请求处理前(视图之前) 7 print(f"请求路径: {request.path}") 8 9 # 获取响应 10 response = get_response(request) 11 12 # 响应处理后(返回客户端之前) 13 print(f"响应状态码: {response.status_code}") 14 15 return response 16 17 return middleware 18
类式中间件:
1class StatsMiddleware: 2 def __init__(self, get_response): 3 self.get_response = get_response 4 # 初始化代码 5 6 def __call__(self, request): 7 # 请求前 8 start_time = time.time() 9 10 response = self.get_response(request) 11 12 # 响应后 13 duration = time.time() - start_time 14 print(f"请求 {request.path} 耗时: {duration:.2f}秒") 15 16 return response 17
4.4 中间件的钩子方法
类式中间件可以定义多个钩子方法:
1class AdvancedMiddleware: 2 def __init__(self, get_response): 3 self.get_response = get_response 4 5 def __call__(self, request): 6 # 请求处理 7 response = self.get_response(request) 8 return response 9 10 def process_view(self, request, view_func, view_args, view_kwargs): 11 """视图调用前执行""" 12 print(f"即将调用视图: {view_func.__name__}") 13 # 可以返回 None 继续处理,或返回 HttpResponse 短路 14 15 def process_exception(self, request, exception): 16 """视图抛出异常时执行""" 17 print(f"捕获异常: {exception}") 18 # 可以返回 HttpResponse 替代错误页面 19 20 def process_template_response(self, request, response): 21 """模板响应渲染前执行""" 22 print("准备渲染模板") 23 return response 24
4.5 激活中间件
将中间件类或函数的完整 Python 路径添加到 settings.py 的 MIDDLEWARE 列表中:
1# settings.py 2MIDDLEWARE = [ 3 'django.middleware.security.SecurityMiddleware', 4 'django.contrib.sessions.middleware.SessionMiddleware', 5 'django.middleware.common.CommonMiddleware', 6 'django.middleware.csrf.CsrfViewMiddleware', 7 'django.contrib.auth.middleware.AuthenticationMiddleware', 8 'django.contrib.messages.middleware.MessageMiddleware', 9 'django.middleware.clickjacking.XFrameOptionsMiddleware', 10 'blog.middleware.StatsMiddleware', # 自定义中间件 11] 12
顺序很重要!例如,AuthenticationMiddleware 依赖于 SessionMiddleware,因为用户信息存储在 session 中。
4.6 实战:IP 访问限制中间件
1# blog/middleware.py 2from django.http import HttpResponseForbidden 3from django.core.cache import cache 4 5class RateLimitMiddleware: 6 """ 7 简单的 IP 限流中间件:同一 IP 每分钟最多访问 60 次 8 """ 9 def __init__(self, get_response): 10 self.get_response = get_response 11 12 def __call__(self, request): 13 # 获取客户端 IP 14 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') 15 if x_forwarded_for: 16 ip = x_forwarded_for.split(',')[0] 17 else: 18 ip = request.META.get('REMOTE_ADDR') 19 20 # 缓存键 21 cache_key = f'rate_limit_{ip}' 22 23 # 获取当前访问次数 24 count = cache.get(cache_key, 0) 25 26 if count >= 60: 27 return HttpResponseForbidden('访问过于频繁,请稍后再试') 28 29 # 增加计数 30 cache.set(cache_key, count + 1, timeout=60) 31 32 # 继续处理请求 33 response = self.get_response(request) 34 return response 35
5. 综合实战:用户认证与表单结合
让我们将所学知识整合起来,实现一个完整的用户资料编辑功能。
5.1 用户资料表单
1# blog/forms.py 2from django import forms 3from django.contrib.auth.models import User 4 5class UserProfileForm(forms.ModelForm): 6 """用户资料表单""" 7 class Meta: 8 model = User 9 fields = ['first_name', 'last_name', 'email'] 10 widgets = { 11 'first_name': forms.TextInput(attrs={'class': 'form-control'}), 12 'last_name': forms.TextInput(attrs={'class': 'form-control'}), 13 'email': forms.EmailInput(attrs={'class': 'form-control'}), 14 } 15
5.2 资料编辑视图
1# blog/views.py 2from django.contrib.auth.decorators import login_required 3from django.contrib import messages 4from .forms import UserProfileForm 5 6@login_required 7def profile_edit(request): 8 if request.method == 'POST': 9 form = UserProfileForm(request.POST, instance=request.user) 10 if form.is_valid(): 11 form.save() 12 messages.success(request, '资料更新成功!') 13 return redirect('profile_edit') 14 else: 15 form = UserProfileForm(instance=request.user) 16 17 # 记录用户最后访问时间到 session 18 request.session['last_visit'] = str(timezone.now()) 19 20 return render(request, 'blog/profile_edit.html', { 21 'form': form, 22 'last_visit': request.session.get('last_visit') 23 }) 24
5.3 中间件记录用户活动
1# blog/middleware.py 2import logging 3logger = logging.getLogger(__name__) 4 5class UserActivityMiddleware: 6 def __init__(self, get_response): 7 self.get_response = get_response 8 9 def __call__(self, request): 10 # 仅记录已登录用户的活动 11 if request.user.is_authenticated: 12 logger.info(f"用户 {request.user.username} 访问 {request.path}") 13 14 # 在 session 中记录最后活动时间 15 request.session['last_activity'] = str(timezone.now()) 16 17 response = self.get_response(request) 18 return response 19
5.4 模板中使用消息和用户信息
1{# blog/templates/blog/profile_edit.html #} 2{% extends "base.html" %} 3 4{% block content %} 5<h2>编辑资料</h2> 6 7{% if messages %} 8 {% for message in messages %} 9 <div class="alert alert-{{ message.tags }}">{{ message }}</div> 10 {% endfor %} 11{% endif %} 12 13{% if last_visit %} 14 <p class="text-muted">上次访问时间: {{ last_visit }}</p> 15{% endif %} 16 17<form method="post"> 18 {% csrf_token %} 19 {{ form.as_p }} 20 <button type="submit" class="btn btn-primary">保存修改</button> 21</form> 22{% endblock %} 23
6. 小结与练习
本篇总结
我们深入学习了 Django 的四个核心主题:
- Form 组件:表单定义、验证、渲染,以及 ModelForm 的使用
- Auth 认证系统:用户模型、认证登录、权限管理、访问控制
- Cookie 与 Session:状态保持机制、session 存储配置、实战应用
- 中间件:执行流程、自定义中间件、钩子方法
练习
- 注册登录功能:实现完整的用户注册、登录、注销功能,并使用
login_required保护文章发布页面。 - 表单验证:为文章创建表单添加自定义验证,禁止标题中包含特定敏感词。
- 访问统计:编写中间件统计每个用户的访问次数,并在用户资料页显示。
- 购物车功能:使用 session 实现一个简单的购物车,支持添加、删除商品。
下一篇,我们将学习 Django 的 FBV 与 CBV、Nginx+uwsgi 生产部署配置,完成整个系列的最后篇章。敬请期待!
《Django 基础入门教程(第四篇):Form组件、Auth认证、Cookie/Session与中间件》 是转载文章,点击查看原文。