在上一篇文章中欧,我们学习了Django视图与路由的基础流程 ------ 通过路由映射请求到视图,用视图函数处理简单逻辑并返回响应。但在实际开发中,我们还需要更高效的工具(如快捷函数)、更灵活的功能扩展(如视图装饰器),以及处理复杂场景(如JSON响应。文件上传)。本篇文章将聚焦这些进阶能力,带你提示Django视图开发的效率与灵活性
一、快捷函数:简化视图开发
Django的 django.shortcuts 模块提供了一组快捷函数,封装了视图开发中常用的重复逻辑(如渲染模板、查询数据、重定向),让你用更少的代码实现更多功能
1.1 render():渲染模版并返回响应
最常用的快捷函数,用于将模板与上下文数据结合,生成 HttpResponse 对象,无需手动创建 HttpResponse ,也无需指定模版路径(Django 会自动在templates 目录下查找)
语法:
python
render(request, template_name, context=None, content_type=None, status=None, using=None)
①request:Http请求对象(必须传) ②template_name:模版文件名(必须传)
③context:传递给模版的上下文字典(键为模版变量名,值为数据) ④status:Http状态码
举例:
python
# myapp/views.py
from django.shortcuts import render
from .models import Article
def article_detail(request, pk):
"""用render()渲染文章详情模板"""
article = Article.objects.get(pk=pk)
# 上下文字典:模板中可通过{{ article.title }}访问数据
context = {
'article': article,
'author': article.author.username, # 关联数据
'is_favorite': request.user.is_authenticated # 业务逻辑判断
}
# 渲染模板并返回响应(无需手动创建HttpResponse)
return render(request, 'article_detail.html', context)
在模版中使用上下文:
html
<!-- article_detail.html -->
<h1>{{ article.title }}</h1>
<p>作者:{{ author }}</p>
{% if is_favorite %}
<button>添加收藏</button>
{% endif %}
<div>{{ article.content }}</div>
1.2 redirect():重定向到其他URL
用于实现页面跳转,支持三种跳转场景:
① 跳转到命名路由 (推荐使用) ② 跳转到带参数的命名路由
③ 跳转到外部URL(如 http://example.com)
语法:
python
redirect(to, *args, permanent=False, **kwargs)
① to:目标URL(可以是命名路由名、路径字符串、外部 URL)
② permanent:是否为永久重定向(True对应301状态码,False对应302,默认为False)
③ args/kwargs:传递给命名路由的参数
举例:
python
# myapp/views.py
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from .models import Article
def add_article(request):
"""添加文章后重定向到文章详情页"""
if request.method == 'POST':
title = request.POST.get('title')
content = request.POST.get('content')
# 创建文章
article = Article.objects.create(title=title, content=content)
# 场景1:跳转到带参数的命名路由(推荐)
return redirect('article_detail', pk=article.pk) # 等价于 reverse('article_detail', args=[article.pk])
# GET请求:显示添加文章表单
return render(request, 'add_article.html')
def redirect_to_home(request):
"""跳转到首页(命名路由为'home')"""
# 场景2:跳转到无参数的命名路由
return redirect('home')
def redirect_to_external(request):
"""跳转到外部URL(如Django官网)"""
# 场景3:跳转到外部链接
return redirect('https://www.djangoproject.com/')
1.3 get_object_or_404():查询对象或返回404
查询单个对象时,若对象不存在,自动抛出 Http404异常(返回404页面),无需手动写 try-except捕获DoesNotExist 异常,代码简洁明了
语法:
python
get_object_or_404(klass, *args, **kwargs)
① klass:模型类(如Article)或查询集
② *args/**kwargs:查询条件
举例:
python
# 优化前:手动try-except
def article_detail_old(request, pk):
try:
article = Article.objects.get(pk=pk)
except Article.DoesNotExist:
raise Http404('文章不存在')
return render(request, 'article_detail.html', {'article': article})
# 优化后:用get_object_or_404()
from django.shortcuts import get_object_or_404
def article_detail(request, pk):
# 若找不到pk=pk的Article,自动返回404
article = get_object_or_404(Article, pk=pk, is_published=True) # 可加额外条件(如仅查询已发布文章)
return render(request, 'article_detail.html', {'article': article})
1.4 get_list_or_404():查询列表或返回404
与 get_object_or_404()类似,但用于查询**多个对象(查询集):**若查询集为空,自动返回 404 页面;若有数据,返回查询集
语法:
python
get_list_or_404(klass, *args, **kwargs)
参数与 get_object_or_404()一致,返回值为查询集(而非单个对象)
举例:
python
from django.shortcuts import get_list_or_404
def published_articles(request):
"""查询已发布的文章列表,为空则返回404"""
# 若没有is_published=True的文章,自动返回404
articles = get_list_or_404(Article, is_published=True)
return render(request, 'article_list.html', {'articles': articles})
二、视图装饰器:增强视图功能
视图装饰器是 Python 装饰器在 Django 视图中的应用,用于在不修改视图函数代码的前提下,为其添加额外功能(如限制 HTTP 方法、要求登录、缓存页面)。Django 提供了多个内置装饰器,也支持自定义
2.1 限制 HTTP 方法:控制请求类型
最常用的装饰器之一,用于限制视图仅接受特定的 HTTP 方法(如仅 GET、仅 POST),避免非法请求(如用 DELETE 方法访问列表页)
常用装饰器:
① @require_http_methods(['方法1', '方法2'])
:仅允许指定的 HTTP 方法
② @require_GET
:仅允许 GET 方法(等价于@require_http_methods(['GET'])
);
③ @require_POST
:仅允许 POST 方法(等价于@require_http_methods(['POST'])
);
④ @require_safe
:仅允许 GET 和 HEAD 方法(安全方法,不修改数据)
举例:
python
# myapp/views.py
from django.views.decorators.http import require_http_methods, require_GET, require_POST
from django.shortcuts import render, redirect
from .models import Article
# 1. 仅允许GET和POST方法
@require_http_methods(['GET', 'POST'])
def edit_article(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == 'GET':
# GET:显示编辑表单
return render(request, 'edit_article.html', {'article': article})
elif request.method == 'POST':
# POST:处理表单提交
article.title = request.POST.get('title')
article.content = request.POST.get('content')
article.save()
return redirect('article_detail', pk=article.pk)
# 2. 仅允许GET方法(显示文章列表)
@require_GET
def article_list(request):
articles = Article.objects.all()
return render(request, 'article_list.html', {'articles': articles})
# 3. 仅允许POST方法(处理表单提交)
@require_POST
def delete_article(request, pk):
article = get_object_or_404(Article, pk=pk)
article.delete()
return redirect('article_list')
效果:当使用不允许的方法访问时(如用 PUT 访问edit_article
),Django 会自动返回405 Method Not Allowed
响应
2.2 @gzip_page
:压缩响应内容
用于对视图返回的响应内容进行 Gzip 压缩,减少网络传输数据量,提升页面加载速度(尤其适合大文本响应,如 HTML、JSON)
举例:
python
from django.views.decorators.gzip import gzip_page
from django.http import HttpResponse
# 压缩响应内容
@gzip_page
def large_response(request):
"""返回大文本响应,自动压缩"""
# 模拟大文本(如长文章、大量数据)
large_text = 'Django 视图装饰器...' * 1000
return HttpResponse(large_text)
注意:仅在生产环境生效,开发环境(DEBUG=True
)下可能不压缩
2.3 其他常用装饰器
(1)@login_required
:要求用户登录
限制视图仅允许已登录用户访问,未登录用户会被重定向到登录页面(需在settings.py
中配置LOGIN_URL
)
举例:
python
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
# 要求登录才能访问个人资料页
@login_required(login_url='/login/') # 未登录时重定向到/login/
def user_profile(request):
# request.user 为当前登录用户(已通过认证)
return render(request, 'profile.html', {'user': request.user})
配置LOGIN_URL
(可选,默认/accounts/login/
):
python
# settings.py
LOGIN_URL = '/login/' # 未登录用户的重定向地址
(2)@permission_required
:要求用户有特定权限
限制视图仅允许具有指定权限的用户访问(如 "修改文章" 权限),未授权用户返回 403 页面
python
from django.contrib.auth.decorators import permission_required
from django.shortcuts import get_object_or_404, render
from .models import Article
# 要求用户有"修改文章"权限(权限名格式:app_label.permission_name)
@permission_required('myapp.change_article', raise_exception=True)
def edit_article(request, pk):
"""仅允许有修改权限的用户编辑文章"""
article = get_object_or_404(Article, pk=pk)
# 编辑逻辑...
return render(request, 'edit_article.html', {'article': article})
① 'myapp.change_article'
:权限名,myapp
是应用名,change_article
是 Django 自动为Article
模型生成的权限(默认有add
/change
/delete
/view
四种权限);
② raise_exception=True
:未授权时直接返回 403,而非重定向
(3)@csrf_exempt
:豁免 CSRF 保护
Django 默认对 POST 请求进行 CSRF 保护(需在表单中添加{% csrf_token %}
),但对于 API 接口(如接收外部服务的 POST 请求),可能需要豁免 CSRF 验证
python
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json
# 豁免CSRF保护(仅用于API接口,谨慎使用)
@csrf_exempt
def api_receive_data(request):
if request.method == 'POST':
# 接收外部POST数据(无需CSRF令牌)
data = json.loads(request.body)
# 处理数据...
return JsonResponse({'status': 'success', 'data': data})
return JsonResponse({'status': 'error'}, status=400)
警告:仅在确认安全的场景下使用(如内部 API),公开接口需用其他认证方式(如 Token)
(4)@cache_page
:缓存页面响应
缓存视图返回的响应,指定时间内重复请求会直接返回缓存结果,无需重新执行视图逻辑(提升高频访问页面的性能)
python
from django.views.decorators.cache import cache_page
from django.shortcuts import render
# 缓存15分钟(单位:秒)
@cache_page(60 * 15)
def popular_articles(request):
"""热门文章列表:15分钟内仅执行一次视图逻辑"""
# 耗时查询(如排序、过滤大量数据)
articles = Article.objects.all().order_by('-views')[:10]
return render(request, 'popular_articles.html', {'articles': articles})
注意:缓存基于 URL,不同 URL(如/popular/?page=1
和/popular/?page=2
)会分别缓存
三、内置视图:直接复用的"现成功能"
Django 提供了一些内置视图,封装了常见场景的完整逻辑(如处理静态文件、错误页面),无需自己编写视图函数,直接配置路由即可使用
3.1 serve
:开发环境处理媒体文件
在开发环境中,Django 默认不处理用户上传的媒体文件(如头像、附件),需用django.views.static.serve
视图手动配置媒体文件的访问路径
配置步骤
python
# 项目主 urls.py
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.views.static import serve
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# 配置媒体文件访问:/media/xxx/ → 映射到settings.MEDIA_ROOT/xxx
path('media/<path:path>/', serve, {'document_root': settings.MEDIA_ROOT}),
]
# (可选)开发环境静态文件配置(Django 1.11+ 可省略,自动处理)
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
配置MEDIA_ROOT
和MEDIA_URL
python
# settings.py
import os
# 媒体文件存储路径(用户上传的文件会保存在这里)
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 媒体文件访问URL前缀(如 /media/avatar.jpg)
MEDIA_URL = '/media/'
注意:仅用于开发环境,生产环境需用 Nginx/Apache 处理媒体文件
3.2 内置错误视图
Django 提供了 4 个内置错误视图,对应 400、403、404、500 错误,可直接复用或自定义模板
|---------------------------|-----------------------------------------|---------|
| 错误类型 | 内置视图 | 作用 |
| 400 Bad Request | django.views.defaults.bad_request | 请求参数错误 |
| 403 Permission Denied | django.views.defaults.permission_denied | 权限不足 |
| 404 Page Not Found | django.views.defaults.page_not_found | 页面不存在 |
| 500 Internal Server Error | django.views.defaults.server_error | 服务器内部错误 |
自定义内置错误视图的模板
无需修改视图,只需在templates
目录下创建对应模板文件(如404.html
),Django 会自动使用自定义模板:
html
<!-- templates/404.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>404 - 页面未找到</title>
</head>
<body>
<h1>抱歉,你访问的页面不存在!</h1>
<a href="{% url 'home' %}">返回首页</a>
</body>
</html>
四、请求与响应对象:处理复杂数据交互
Django 封装了HttpRequest
(请求)和HttpResponse
(响应)对象,用于处理 HTTP 请求的所有信息(如参数、头信息、文件)和返回多样化的响应(如 HTML、JSON、文件)
4.1 HttpRequest
对象:获取请求的所有信息
HttpRequest
对象由 Django 自动创建,作为视图函数的第一个参数(通常命名为request
),包含请求的所有信息
核心属性与方法
属性 / 方法 | 描述 | 示例 |
---|---|---|
request.method |
HTTP 请求方法(大写字符串,如'GET' 、'POST' ) |
if request.method == 'POST': |
request.GET |
GET 请求参数(QueryDict 对象,类似字典) |
page = request.GET.get('page', 1) |
request.POST |
POST 请求参数(表单数据,QueryDict 对象) |
title = request.POST.get('title') |
request.META |
请求头信息(字典,键为大写,前缀HTTP_ ) |
user_agent = request.META.get('HTTP_USER_AGENT') |
request.COOKIES |
请求中的 Cookie(字典) | session_id = request.COOKIES.get('sessionid') |
request.FILES |
上传的文件(MultiValueDict 对象) |
avatar = request.FILES.get('avatar') |
request.user |
当前登录用户(User 对象,未登录为AnonymousUser ) |
if request.user.is_authenticated: |
request.path |
请求的路径(不含域名和查询参数) | /article/1/ |
实战示例:获取请求数据
python
def handle_request(request):
# 1. 获取请求方法
method = request.method # 'GET' 或 'POST'
# 2. 获取GET参数(如 /search/?q=django)
search_query = request.GET.get('q', '') # 默认值为空字符串
# 3. 获取POST参数(表单提交)
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 4. 获取请求头(如用户代理)
user_agent = request.META.get('HTTP_USER_AGENT') # 浏览器信息
# 5. 获取上传文件(如头像)
avatar = request.FILES.get('avatar') # 需表单设置 enctype="multipart/form-data"
# 6. 获取当前用户
if request.user.is_authenticated:
user_info = f"当前用户:{request.user.username}"
else:
user_info = "未登录"
return HttpResponse(f"方法:{method}<br>查询:{search_query}<br>{user_info}")
4.2 HttpResponse
及其子类:返回多样化响应
HttpResponse
是所有响应对象的基类,Django 还提供了多个子类,用于返回特定类型的响应(如 JSON、文件)
(1)基础HttpResponse
:返回文本响应
用于返回简单文本、HTML 等响应,可设置响应头和 Cookie
python
from django.http import HttpResponse
def simple_response(request):
# 1. 基础文本响应
response = HttpResponse('Hello, Django!', status=200)
# 2. 设置响应头
response['Content-Type'] = 'text/plain' # 响应类型
response['X-Custom-Header'] = 'MyValue' # 自定义头
# 3. 设置Cookie(max_age单位:秒)
response.set_cookie('name', 'Django', max_age=3600) # 1小时有效期
# 4. 删除Cookie
response.delete_cookie('old_name')
return response
(2)JsonResponse
:返回 JSON 响应
专门用于返回 JSON 格式的响应(如 API 接口),自动设置Content-Type: application/json
,无需手动序列化字典
python
from django.http import JsonResponse
def api_data(request):
# 字典数据(自动序列化为JSON)
data = {
'status': 'success',
'data': {
'articles': [
{'id': 1, 'title': 'Django路由'},
{'id': 2, 'title': 'Django视图'}
]
}
}
# 返回JSON响应(safe=True表示data必须是字典,否则需设为False)
return JsonResponse(data)
返回列表数据(需设safe=False
):
python
def api_list(request):
articles = [{'id': 1, 'title': 'Django'}, {'id': 2, 'title': 'Python'}]
return JsonResponse(articles, safe=False) # 列表需设safe=False
(3)StreamingHttpResponse
:流式传输大文件
用于返回大型文件(如视频、压缩包),避免一次性加载整个文件到内存,而是分块传输
python
from django.http import StreamingHttpResponse
import os
def stream_large_file(request):
# 大文件路径
file_path = os.path.join(settings.MEDIA_ROOT, 'large_file.mp4')
file_size = os.path.getsize(file_path)
# 定义文件迭代器:分块读取文件
def file_iterator(file_path, chunk_size=8192):
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size) # 每次读取8KB
if not chunk:
break
yield chunk
# 流式响应
response = StreamingHttpResponse(
file_iterator(file_path),
content_type='video/mp4' # 视频文件类型
)
# 设置文件大小(可选,用于进度条)
response['Content-Length'] = str(file_size)
return response
(4)FileResponse
:返回文件下载
StreamingHttpResponse
的子类,专门用于返回文件下载,自动处理文件读取和响应头设置
python
from django.http import FileResponse
import os
def download_file(request):
# 文件路径
file_path = os.path.join(settings.MEDIA_ROOT, 'document.pdf')
# 打开文件(rb:二进制只读)
file = open(file_path, 'rb')
# 文件响应:自动设置Content-Type和下载头
response = FileResponse(
file,
content_type='application/pdf' # PDF文件类型
)
# 设置Content-Disposition:触发浏览器下载(而非预览)
response['Content-Disposition'] = 'attachment; filename="document.pdf"'
return response
五、模板响应对象:延迟渲染模板
普通render()
会立即渲染模板并返回响应,而 "模板响应对象"(SimpleTemplateResponse
、TemplateResponse
)允许在返回响应前延迟渲染模板,并对模板进行后期处理(如修改上下文、添加响应头)
5.1 SimpleTemplateResponse
:基础延迟渲染
python
from django.template.response import SimpleTemplateResponse
def delayed_render(request):
# 1. 创建模板响应对象(未渲染)
context = {'name': 'Django'}
response = SimpleTemplateResponse('index.html', context)
# 2. 延迟处理:返回前修改上下文
response.context_data['version'] = '5.0' # 模板中可访问{{ version }}
# 3. 返回响应(此时才渲染模板)
return response
5.2 TemplateResponse
:支持请求上下文
SimpleTemplateResponse
的子类,自动包含请求上下文(如request
对象、user
对象),无需手动传递
python
from django.template.response import TemplateResponse
def template_response(request):
# 创建TemplateResponse,自动包含请求上下文
response = TemplateResponse(request, 'index.html', {'name': 'Django'})
# 添加"渲染后回调":模板渲染完成后执行
def post_render_callback(response):
# 在响应内容末尾添加注释
response.content += b' <!-- 渲染完成 -->'
return response
# 注册回调函数
response.add_post_render_callback(post_render_callback)
return response
六、文件上传:实现用户上传功能
Django 支持用户上传文件(如头像、附件),需通过 "表单 + 视图" 配合实现,核心是处理request.FILES
中的上传文件
6.1 步骤 1:定义文件上传表单
需在表单中设置enctype="multipart/form-data"
(否则无法传递文件数据),可使用 Django 的Form
类简化表单验证
python
# myapp/forms.py
from django import forms
class UploadAvatarForm(forms.Form):
"""用户头像上传表单"""
# FileField:文件上传字段,自动验证文件类型
avatar = forms.FileField(
label="选择头像",
help_text="支持JPG、PNG格式,不超过5MB"
)
# 可选:添加其他字段(如用户名)
username = forms.CharField(max_length=100, label="用户名")
6.2 步骤 2:编写上传视图
处理 GET 请求(显示表单)和 POST 请求(处理文件上传),将上传的文件保存到指定目录(如media/avatars/
)
python
# myapp/views.py
from django.shortcuts import render, redirect
from .forms import UploadAvatarForm
import os
from django.conf import settings
def upload_avatar(request):
if request.method == 'POST':
# 1. 绑定表单数据(request.POST 为普通字段,request.FILES 为文件字段)
form = UploadAvatarForm(request.POST, request.FILES)
if form.is_valid():
# 2. 获取表单数据
username = form.cleaned_data['username']
avatar_file = request.FILES['avatar'] # 或 form.cleaned_data['avatar']
# 3. 保存文件到 media/avatars/ 目录
# 构造保存路径(避免文件名重复,可加用户名前缀)
save_dir = os.path.join(settings.MEDIA_ROOT, 'avatars')
# 确保目录存在(不存在则创建)
os.makedirs(save_dir, exist_ok=True)
# 保存文件(分块写入,避免内存溢出)
with open(os.path.join(save_dir, f"{username}_avatar.jpg"), 'wb+') as destination:
for chunk in avatar_file.chunks(): # 分块读取上传文件
destination.write(chunk)
# 4. 上传成功,重定向到结果页
return redirect('upload_success')
else:
# GET请求:显示空表单
form = UploadAvatarForm()
# 渲染表单页面
return render(request, 'upload_avatar.html', {'form': form})
def upload_success(request):
"""上传成功页面"""
return render(request, 'upload_success.html')
6.3 步骤 3:编写上传模板
html
<!-- templates/upload_avatar.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>上传头像</title>
</head>
<body>
<h1>上传用户头像</h1>
<!-- 必须设置 enctype="multipart/form-data" 才能传递文件 -->
<form method="post" enctype="multipart/form-data">
{% csrf_token %} <!-- CSRF保护 -->
{{ form.as_p }} <!-- 渲染表单字段(含标签和错误提示) -->
<button type="submit">上传</button>
</form>
</body>
</html>
<!-- templates/upload_success.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>上传成功</title>
</head>
<body>
<h1>头像上传成功!</h1>
<a href="{% url 'upload_avatar' %}">继续上传</a>
</body>
</html>
6.4 步骤 4:配置媒体文件
确保settings.py
中配置了媒体文件路径(参考 3.1 节),并在urls.py
中配置媒体文件访问路由
七、总结
本篇覆盖了 Django 视图开发的核心进阶能力,总结如下:
1、快捷函数 :render()
、redirect()
、get_object_or_404()
等简化重复逻辑,提升开发效率
2、视图装饰器:控制 HTTP 方法、要求登录 / 权限、缓存页面,增强视图功能
3、请求与响应对象:处理复杂请求数据(如 GET/POST 参数、文件),返回多样化响应(JSON、文件)
4、文件上传:通过表单 + 视图 + 媒体文件配置,实现用户文件上传功能
掌握这些内容后,你已经能应对大多数 Django 视图开发场景 ------ 从简单的页面渲染到复杂的 API 接口、文件处理。结合之前学习的模型与路由知识,你可以搭建起完整的 Django Web 应用骨架,为后续学习模板美化、用户认证等内容打下坚实基础