第三章:视图(Views) 3.1 视图函数(Function-Based Views,FBV) 在 Django Web 框架中,视图是处理用户请求并返回响应的核心组件,承担模型(Models)与模板(Templates)之间的协调职责。视图接收 HTTP 请求,执行业务逻辑(如数据库查询、权限校验、表单验证),组织数据,并最终生成 HTTP 响应返回给客户端。Django 提供两种主流视图实现方式:视图函数(Function-Based Views,FBV) 和 类视图(Class-Based Views,CBV)。本节聚焦于视图函数——Django 最基础、最直观、应用最广泛的视图编写范式,适用于快速开发、逻辑清晰的业务场景及初学者系统性入门。 3.1.
在 Django Web 框架中,视图是处理用户请求并返回响应的核心组件,承担模型(Models)与模板(Templates)之间的协调职责。视图接收 HTTP 请求,执行业务逻辑(如数据库查询、权限校验、表单验证),组织数据,并最终生成 HTTP 响应返回给客户端。Django 提供两种主流视图实现方式:视图函数(Function-Based Views,FBV) 和 类视图(Class-Based Views,CBV)。本节聚焦于视图函数——Django 最基础、最直观、应用最广泛的视图编写范式,适用于快速开发、逻辑清晰的业务场景及初学者系统性入门。
视图函数是使用标准 Python 函数语法编写的可调用对象,其接口契约明确:接收一个 HttpRequest 实例作为唯一必需参数,返回一个 HttpResponse 或其子类的实例。
该设计遵循单一职责原则,将请求处理流程封装为可预测、可测试、可组合的单元。其核心价值在于降低认知负荷,使开发者能以自然的函数式思维组织 Web 逻辑。
简洁性与可读性高
无框架抽象层干扰,代码即逻辑,适合快速验证、原型开发与教学演示。
高度灵活
不受类继承结构约束,可自由引入任意第三方库、自定义中间件或异步操作(配合 async def)。
测试友好
作为纯函数(或接近纯函数),可直接实例化 HttpRequest 进行单元测试,无需启动 Web 服务器或模拟请求生命周期。
渐进式演进支持
可平滑升级为 CBV,或与装饰器(如 @login_required、@csrf_exempt)组合增强功能。
以下为符合 Django 最佳实践的视图函数骨架:
from django.http import HttpResponse def example_view(request): """ 示例视图:处理请求并返回响应 """ # 1. 请求解析与校验(如方法判断、参数提取) if request.method != 'GET': return HttpResponse('仅支持 GET 请求', status=405) # 2. 业务逻辑执行(数据查询、计算、外部服务调用等) user_data = {'name': 'Alice', 'role': 'admin'} # 3. 响应构建(内容、状态码、头信息) response = HttpResponse( f"<h1>欢迎,{user_data['name']}!</h1>", content_type='text/html', status=200 ) response['X-Frame-Options'] = 'DENY' # 4. 响应返回 return response
| 组成部分 | 说明 | 最佳实践 |
|---|---|---|
request 参数 |
HttpRequest 对象,封装全部客户端请求上下文(方法、路径、参数、头、会话、用户等) |
始终作为首个参数;避免修改 request 对象本身 |
| 请求方法路由 | 使用 request.method 区分 GET/POST/PUT 等操作 |
优先用 if/elif/else 显式分支,避免隐式逻辑 |
| 业务逻辑区 | 数据获取、处理、验证、权限检查等核心操作 | 保持职责单一;复杂逻辑应抽离至 services/ 或 utils/ 模块 |
| 响应构造 | 使用 HttpResponse 或其专用子类返回结果 |
优先选用语义化子类(如 JsonResponse、redirect);显式设置 status 与 content_type |
| 返回语句 | 必须返回 HttpResponse 实例(不可返回字符串、字典或 None) |
确保所有代码路径均有返回值,避免未定义行为 |
HttpRequest 是 Django 请求处理的入口数据载体,提供对客户端请求全量信息的安全访问接口。
| 属性 | 类型 | 说明 | 使用示例 |
|---|---|---|---|
method |
str |
HTTP 方法(大写) | if request.method == 'POST': |
path |
str |
请求路径(不含查询参数与域名) | '/articles/123/' |
get_full_path() |
str |
完整路径(含查询参数) | '/search/?q=django&page=1' |
GET |
QueryDict |
GET 参数字典(只读) | request.GET.get('page', '1') |
POST |
QueryDict |
POST 表单参数(需 enctype="application/x-www-form-urlencoded") |
request.POST.get('username') |
FILES |
MultiValueDict |
上传文件集合(需 enctype="multipart/form-data") |
request.FILES.get('avatar') |
headers |
dict |
请求头(键自动转为大驼峰,如 'User-Agent') |
request.headers.get('X-Requested-With') |
COOKIES |
dict |
客户端 Cookie 字典 | request.COOKIES.get('sessionid') |
session |
SessionBase |
用户会话对象(需启用 SessionMiddleware) |
request.session['cart_items'] = 3 |
user |
User / AnonymousUser |
当前认证用户(需启用 AuthenticationMiddleware) |
if request.user.is_authenticated: |
META |
dict |
服务器元数据(含 REMOTE_ADDR、HTTP_USER_AGENT 等) |
request.META.get('REMOTE_ADDR') |
is_ajax():检测是否为 AJAX 请求(检查 HTTP_X_REQUESTED_WITH 头)is_secure():判断是否为 HTTPS 协议get_host():获取请求主机名(含端口)build_absolute_uri():构建完整绝对 URL(用于邮件链接、API 返回)安全提示:所有用户输入(
GET、POST、FILES、COOKIES)均视为不可信数据,必须经过校验、清洗或转义后方可使用,尤其在拼接 SQL、HTML 或执行系统命令时。
Django 提供分层响应类体系,覆盖 Web 开发中绝大多数响应场景,确保语义清晰、类型安全、开箱即用。
| 类名 | 适用场景 | 核心参数 | 典型用法 |
|---|---|---|---|
HttpResponse |
基础文本响应 | content, content_type, status |
HttpResponse('Hello', content_type='text/plain') |
JsonResponse |
JSON API 响应 | data, safe, json_dumps_params |
JsonResponse({'ok': True}) |
redirect()(快捷函数) |
重定向(302) | to, permanent=False |
redirect('home') 或 redirect('/login/') |
HttpResponseRedirect |
显式 302 重定向 | redirect_to |
HttpResponseRedirect('/success/') |
HttpResponsePermanentRedirect |
301 永久重定向 | redirect_to |
SEO 友好重定向 |
HttpResponseNotFound |
404 响应 | content, content_type |
HttpResponseNotFound('<h1>Not Found</h1>') |
HttpResponseServerError |
500 响应 | content, content_type |
自定义错误页 |
FileResponse |
文件下载 | open_file, filename, as_attachment |
FileResponse(open('report.pdf', 'rb'), as_attachment=True) |
StreamingHttpResponse |
大文件/实时流响应 | streaming_content, content_type |
日志流、CSV 导出 |
实践建议:优先使用
JsonResponse替代HttpResponse(content=json.dumps(...));用redirect()替代HttpResponseRedirect;文件下载务必设置as_attachment=True并指定filename,确保浏览器正确识别。
Django 通过 urls.py 文件定义 URL 模式与视图函数的映射关系,实现请求分发。现代 Django(2.0+)推荐使用 path() 函数,其语义清晰、支持类型转换、避免正则复杂度。
path() 函数详解from django.urls import path from . import views urlpatterns = [ # 基础路径 path('hello/', views.hello_view, name='hello'), # 带整型参数 path('articles/<int:article_id>/', views.article_detail, name='article_detail'), # 带字符串参数(不含 '/') path('users/<str:username>/', views.user_profile, name='user_profile'), # 带 slug 参数(字母/数字/下划线/连字符) path('posts/<slug:post_slug>/', views.post_detail, name='post_detail'), # 带 UUID 参数 path('orders/<uuid:order_id>/', views.order_status, name='order_status'), # 匹配任意路径(含 '/') path('files/<path:file_path>/', views.serve_file, name='serve_file'), ]
| 转换器 | 匹配规则 | 传递给视图的类型 |
|---|---|---|
<int:name> |
0 或正整数 | int |
<str:name> |
非空字符串(不含 /) |
str |
<slug:name> |
ASCII 字母、数字、_、- |
str |
<uuid:name> |
标准 UUID 格式(如 123e4567-e89b-12d3-a456-426614174000) |
uuid.UUID |
<path:name> |
任意非空字符串(可含 /) |
str |
关键原则:URL 名称(
name参数)是反向解析的唯一标识,模板中使用{% url 'article_detail' article_id=42 %},视图中使用reverse('article_detail', args=[42]),彻底解耦 URL 字符串硬编码。
实际项目中,FBV 常与以下 Django 核心机制协同工作:
render() 函数from django.shortcuts import render def product_list_view(request): products = Product.objects.filter(is_active=True) # ORM 查询 context = { 'products': products, 'title': '商品列表' } return render(request, 'products/list.html', context)
render() 封装了 HttpResponse + loader.get_template().render(),自动处理模板查找、上下文渲染、CSRF token 注入。Model.objects.filter(), get(), all(), values()Model.objects.create(), form.save()instance.save(), Model.objects.update()instance.delete(), Model.objects.filter().delete()Form 类驱动from django import forms class ContactForm(forms.Form): name = forms.CharField(max_length=100) email = forms.EmailField() message = forms.CharField(widget=forms.Textarea) def contact_view(request): if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): # 处理有效数据 send_email(form.cleaned_data['email'], form.cleaned_data['message']) return redirect('contact_success') else: form = ContactForm() return render(request, 'contact.html', {'form': form})
from django.contrib.auth import login; login(request, user)from django.contrib.auth import logout; logout(request)@login_required, @permission_required('app.add_model'){% if user.is_authenticated %}...{% endif %}# 存储 request.session['preferred_language'] = 'zh-hans' request.session.set_expiry(3600) # 1小时后过期 # 读取 lang = request.session.get('preferred_language', 'en-us') # 删除 del request.session['preferred_language']
from django.http import Http404 from django.core.exceptions import PermissionDenied def sensitive_view(request): if not request.user.has_perm('app.view_sensitive'): raise PermissionDenied("无权访问此资源") try: obj = ExpensiveModel.objects.get(pk=1) except ExpensiveModel.DoesNotExist: raise Http404("对象不存在") return HttpResponse("敏感内容")
raise Http404 触发 404 页面(由 handler404 处理)raise PermissionDenied 触发 403 页面(由 handler403 处理)try/except Exception:,应捕获具体异常类型| 维度 | 优势 | 局限 | 适用建议 |
|---|---|---|---|
| 学习曲线 | 零抽象层,Python 基础即可上手 | — | 初学者首选,教学场景最优 |
| 代码组织 | 单文件、单函数、逻辑线性 | 复杂视图易臃肿(如 CRUD 集合) | 简单页面(首页、联系页、静态页);单一职责视图(登录、注册) |
| 复用性 | 通过函数组合、装饰器复用 | 无内置继承/混入机制 | 共享逻辑抽离为工具函数或装饰器(如 @require_http_methods(['GET'])) |
| HTTP 方法处理 | if request.method == 'POST': 直观 |
多方法分支导致嵌套加深 | 轻量级表单提交;避免在单视图中混合过多方法逻辑 |
| 可测试性 | HttpRequest 可手动构造,断言直接 |
— | 单元测试覆盖率易达 90%+,CI/CD 友好 |
决策指南:
✅ 选择 FBV:逻辑简单(<50 行)、功能单一(如“发送邮件”、“下载报告”)、团队熟悉函数式编程、MVP 快速验证。
⚠️ 考虑 CBV:需复用通用逻辑(如列表页分页、详情页对象加载)、多 HTTP 方法统一处理(如 RESTful API)、项目已采用 CBV 规范、追求更高内聚性。
from django.http import HttpResponse, HttpResponseNotFound def health_check(request): """健康检查端点,返回 200 OK""" return HttpResponse('OK', status=200) def legacy_redirect(request): """301 重定向至新路径""" from django.urls import reverse new_url = reverse('new_home') return HttpResponsePermanentRedirect(new_url)
from django.http import HttpResponseNotFound from django.shortcuts import get_object_or_404 def article_detail(request, article_id): # 使用 get_object_or_404 替代手动 try/except article = get_object_or_404(Article, id=article_id, is_published=True) # 渲染 HTML 片段 html = f""" <article> <h1>{article.title}</h1> <p>{article.content[:200]}...</p> <small>发布于 {article.pub_date}</small> </article> """ return HttpResponse(html, content_type='text/html')
from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt import json @csrf_exempt def api_search(request): if request.method != 'POST': return JsonResponse({'error': '仅支持 POST'}, status=405) try: data = json.loads(request.body) query = data.get('q', '').strip() if not query: return JsonResponse({'error': '缺少搜索关键词'}, status=400) # 模拟搜索(实际应调用 ORM 或 Elasticsearch) results = [ {'id': 1, 'title': f'关于 {query} 的文章'}, {'id': 2, 'title': f'{query} 最佳实践'} ] return JsonResponse({'results': results}) except json.JSONDecodeError: return JsonResponse({'error': '无效 JSON'}, status=400)
from django.http import FileResponse from django.shortcuts import get_object_or_404 def download_report(request, report_id): report = get_object_or_404(Report, id=report_id, owner=request.user) # 打开文件(确保路径安全,禁止用户控制路径) file_path = f'/var/reports/{report.id}.pdf' response = FileResponse( open(file_path, 'rb'), as_attachment=True, filename=f'report-{report.id}.pdf', content_type='application/pdf' ) response['Content-Length'] = report.file_size return response
视图函数(FBV)是 Django 构建 Web 应用的基石,其以 Python 函数的纯粹性、明确的输入输出契约、与框架深度集成的响应生态,成为处理绝大多数 Web 交互场景的首选方案。掌握 HttpRequest 的全量能力、HttpResponse 子类的语义化使用、path() 路由的精准配置,以及与模板、ORM、表单、认证等模块的协同模式,是构建健壮、可维护 Django 应用的关键能力。
后续章节将深入探讨类视图(CBV)的设计哲学与高级用法,帮助开发者在项目规模增长、逻辑复杂度提升时,实现代码结构的优雅演进。