装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能(见一文看懂Python系列之装饰器)。装饰器广泛用于权限校验和缓存等场景,是学习Python Web开发的必备知识。Django项目中使用装饰器可以让代码将变得更干净、更可读、更可维护。今天小编我就带你看下Django自带的常用装饰器的应用场景及正确使用方法。
权限验证
@login_required
@login_required是Django最常用的一个装饰器。其作用是在执行视图函数前先检查用户是否通过登录身份验证,并将未登录的用户重定向到指定的登录url。其中login_url是可选参数。如果不设置,默认login_url是settings.py里设置的LOGIN_URL。
from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
...
@login_required还可以一个可选参数是redirect_field_name, 默认值是'next'。
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
...
注意:
login_required装饰器不会检查用户是否是is_active状态。如果你想进一步限制登录验证成功的用户对某些视图函数的访问,你需要使用更强大的@user_passes_test装饰器。
@user_passes_test
@user_passes_test装饰器的作用是对登录用户对象的信息进行判断,只有通过测试(返回值为True)的用户才能访问视图函数。不符合条件的用户会被跳转到指定的登录url。
@user_passes_test装饰器有一个必选参数,即对用户对象信息进行判断的函数。该函数必需接收user对象为参数。与@login_required类似,@user_passes_test还有两个可选参数(login_url和redirect_field_name),这里就不多讲了。
user_passes_test(func[,login_url=None, redirect_field_name=REDIRECT_FIELD_NAME])
下例中@user_passes_test装饰器对用户的email地址结尾进行判断,会把未通过测试的用户会定向到登录url。试想一个匿名用户来访问,她没有email地址,显然不能通过测试,登录后再来吧。
from django.contrib.auth.decorators import user_passes_test
def email_check(user):
return user.email.endswith('@example.com')
@user_passes_test(email_check)
def my_view(request):
...
如果需要加可选参数,可以按如下方式使用。
@user_passes_test(email_check, login_url='/login/'):
def my_view(request):
...
注意:
@user_passes_test不会自动的检查用户是否是匿名用户, 但是@user_passes_test装饰器还是可以起到两层校验的作用。一来检查用户是否登录,二来检查用户是否符合某些条件,无需重复使用@login_required装饰器。
我们如果只允许is_active的登录用户访问某些视图,我们现在可以使用@user_passes_test装饰器轻松地解决这个问题,如下所示:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.is_active)
def my_view(request):
...
@permission_required
@permission_required装饰器的作用是检查用户用户是否有特定权限,第一个参数perm是权限名,为必选, 第二个参数login_url为可选。
permission_required(perm[, login_url=None, raise_exception=False])
下例检查用户是否有polls.can_vote的权限,没有的话定向至login_url。如果你设置了raise_exception=True, 会直接返回403无权限的错误,而不会跳转到登录页面。那么问题来了,我们需要先使用@login_required来验证用户是否登录,再使用@permission_required装饰器来查看登录用户是否具有相关权限吗? 答案是不需要。如果一个匿名用户来访问这个视图,显然该用户没有相关权限,会自动定向至登录页面。
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url='/login/')
def my_view(request):
...
更多Django权限介绍见Django基础(23): 权限管理(permissions)与用户组(group)详解
缓存
缓存是Django装饰器很重要的一个应用场景。下面我们来看几个主要的缓存装饰器。注意: 使用以下装饰器的前提是你已经对缓存进行了相关设置(见Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用)。
@cache_page
该装饰器可以接收缓存的时间作为参数,比如下例缓存页面15分钟。
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
@cache_control
通常用户将会面对两种缓存: 他或她自己的浏览器缓存(私有缓存)以及他或她的提供者缓存(公共缓存)。 公共缓存由多个用户使用,而受其它人的控制。 这就产生了你不想遇到的敏感数据的问题,比如说你的银行账号被存储在公众缓存中。 因此,Web 应用程序需要以某种方式告诉缓存那些数据是私有的,哪些是公共的。cache_control 装饰器可以解决这个问题。
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
# ...
该修饰器负责在后台发送相应的 HTTP 头部。还有一些其他方法可以控制缓存参数。 例如, HTTP 允许应用程序执行如下操作:
在 Django 中,可使用 cache_control 视图修饰器指定这些缓存参数。 在下例中, cache_control 告诉缓存对每次访问都重新验证缓存并在最长 3600 秒内保存所缓存版本。
from django.views.decorators.cache import cache_control
@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
# ...
在 cache_control() 中,任何合法的Cache-Control HTTP 指令都是有效的。下面是完整列表:
@vary_on_headers
缺省情况下,Django 的缓存系统使用所请求的路径(如blog/article/1)来创建其缓存键。这意味着不同用户请求同样路径都会得到同样的缓存版本,不考虑客户端user-agent, cookie和语言配置的不同, 除非你使用Vary头部通知缓存机制需要考虑请求头里的cookie和语言的不同。
要在 Django 完成这项工作,可使用便利的 vary_on_headers 视图装饰器。例如下面代码告诉Django读取缓存数据时需要同时考虑User-Agent和Cookie的不同。与此类似的装饰器还有@vary_on_cookie。
from django.views.decorators.vary import vary_on_headers
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
...
@never_cache
如果你想用头部完全禁掉缓存, 你可以使用@never_cache装饰器。如果你不在视图中使用缓存,服务器端是肯定不会缓存的,然而用户的客户端如浏览器还是会缓存一些数据,这时你可以使用never_cache禁用掉客户端的缓存。
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
# ...
其它常用装饰器
@method_decorator
前面的案例中,我们的装饰器都是直接使用在函数视图上的。如果需要在基于类的视图上使用装饰器,我们需要使用到@method_decorator这个装饰器, 它的作用是将类伪装成函数方法。@method_decorator第一个参数一般是需要使用的装饰器名。
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decoratorfrom django.views.generic import TemplateView
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
@require_http_methods
该装饰器的作用是限制用户的请求方法。如下例中仅接收GET和POST方法。与此类似的装饰器还有@require_POST, @require_GET和@require_safe。
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def my_view(request):
# Only accept GET or POST method
pass
@gzip_page
该装饰器可以压缩内容,前提是用户客户端允许内容压缩的话。使用方法如下:
from django.views.decorators.gzip import gzip_page
@gzip_page
def my_view(request):
# Only accept GET or POST method
pass
使用多重装饰器
你可以在一个函数或基于类的视图上使用多重装饰器,但一定要考虑装饰器执行的先后顺序。比如下例中会先执行@never_cache, 再执行@login_required。
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
上例等同于:
decorators = [never_cache, login_required]
@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
小结
本文总结了Django自带的常用装饰器的应用场景及如何正确使用它们。下文我们将介绍如何自定义Django装饰器并分享几个常用的自定义装饰器,欢迎关注。
大江狗
2019.3
Django Web开发核心基础知识
Django网站开发四件套是如何遵循MVC软件设计模式的?
Django基础核心技术介绍(1): Model模型的介绍与设计
Django基础核心技术介绍(2): URL的设计与配置
Django基础核心技术介绍(3): View视图详解与通用视图
Django基础核心技术介绍(4): Template模板的编写及过滤器
Django基础核心结束介绍(5): Forms表单的使用与设计
Django基础(6): 模型Models高级进阶必读。
Django基础(7): cookie和session应用场景及如何使用
Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用
Django基础(9): 表单Forms的高级使用技巧
Django基础(10): URL重定向的HttpResponseDirect, redirect和reverse的用法
Django基础(11): 表单集合Formset的高级用法
Django基础(12): Request对象详解及开发显示用户IP地址和浏览器APP
Django基础(13): 深夜放干货。QuerySet特性及高级使用技巧,如何减少数据库的访问,节省内存,提升网站性能。
Django基础(14): 通过next参数实现登录后跳转回到前一页的3种方法
Django基础(15): 模板过滤器(filter)的工作原理及如何自定义模板过滤器
Django基础(16): 模板标签(tags)的分类及如何自定义模板标签
Django基础(17): 如何上传处理文件及Ajax文件上传示范(附GitHub源码)
Django基础(18): 实现文件下载的3种方法及文件私有化
Django基础(19): Django Admin管理后台详解(上)
Django基础(20): Django admin管理后台详解(中)如何自定义list_display和list_filter
Django基础(21): Django admin管理后台详解(下)如何自定义actions, 表单和美化admin
Django基础(22): 数据库的设计之自定义表名,建立索引和使用多数据库主从配置
Django基础(23): 权限管理(permissions)与用户组(group)详解
Django基础(24): aggregate和annotate方法使用详解与示例
Django基础(25):settings.py设置选项深入解读。大江狗精品原创。
Django基础(26): 常用装饰器应用场景及正确使用方法
Django基础(27): 快捷函数(shortcut function)模块详解Django基础(28): 如何设计充满陷阱的优美URL
Django基础(28): 如何设计充满陷阱的优美URL
Django Web开发实战案例
Django 2.0 项目实战(1): 扩展Django自带User模型,实现用户注册与登录
Django 2.0 项目实战(2): 编辑用户个人资料,扩展Django后台UserAdmin
Django 2.0项目实战(3): 密码重置与退出登录
Django 2.0 项目实战: 图片上传与显示
Django 2.0 项目实战: PDF文件页面提取
Django 2.0 项目实战: PDF文件合并
Django 2.0 项目实战:输出树形分类目录
Django 2.0 项目实战: 网页计数器统计浏览次数
Django 2.0 项目实战: 利用AJAX实现博文实时搜索
Django 1.X和2.0下利用自带分页Paginator类实现分页功能
Django实战: 利用Ajax生成联动下拉菜单
世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(1)
世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(2)
django-allauth教程(1): 安装,用户注册,登录,邮箱验证和密码重置(更新)
django-allauth教程(2): 用户个人资料UserProfile扩展与编辑
django-allauth教程(3): 第三方账户授权登录(以百度账号为例)
django-allauth教程(4): 美化模板,自定义邮件和消息内容
Django+jQuery cropper实现用户头像裁剪, 预览和上传[原创]
Django实战教程: 开发餐厅在线点评网站(1)
Django实战教程: 开发餐厅在线点评网站(2)
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(1)
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(2)之权限管理
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(3)附GitHub代码地址
Django实战专题: 开发专业博客(1)之内容管理后台开发
Django实战专题: 开发专业博客(2)之母子类别导航和添加富文本编辑器CKEditor
Django实战专题: 开发专业博客(3)之仿微信评论点赞功能
Django实战: Python爬取链家上海二手房信息,存入数据库并在前端显示
Django应用实战: 编写你自己的PDF编辑器, 实现PDF页面提取, 页面合并与替换。
如何在阿里云Ubuntu服务器通过uWSGI和Nginx部署Django项目教程-大江狗原创出品
Django Web开发学习笔记
浅谈Django Model创建对象的save与create方法
Django模板设置全局变量(默认变量)
Django常用命令django-admin.py和manage.py用法详解
Django自定义图片和文件上传路径(upload_to)的2种方式
Django ContentTypes框架详解及使用场景介绍
Django更改模型过程中易出现的问题及解决方案
2019新年第一篇: SQLite的优缺点及Django配置MySQL数据库