废话不讲,只唠干货!
一、引子:在类视图中实现login_required 功能
在函数视图和类视图中实现login_required功能都臭了了大街了,细节不再描述。仅仅用类视图作为切入点。
1.定义LoginRequiredMixin实现校验的动作:
class LoginRequiredMixin(object):
"""
定义LoginRequiredMixin,实现验证用户登录的逻辑
"""
@classmethod
def as_view(cls, **initkwargs):
"""
重写as_view方法,进行校验
:param initkwargs:
:return:
"""
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
return login_required(view)
2.在类视图中继承LoginRequiredMixin
class UserIndexView(LoginRequiredMixin,View):
def get(self, request):
return render(request, "user_index.html")
即可实现,只有登录的用户才能访问user_index.html的功能。
二、上面已经实现了LoginRequired的功能,原理是什么?
有些程序员说我知道,不就是在LoginRequiredMixin实现对login_required的封装嘛,这又有什么价值写成博客呢?
不过,你确定你真的知道吗?
那么login_required是如何实现的呢?且听我慢慢道来。
解析django.contrib.auth中的decorators.py
decorators模块中只有定义了3个装饰器函数(user_passes_test、 login_required、 permission_required)
1.user_passes_test
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
源码不再解析,有兴趣的可以去看看。这个函数的主要作用就是对给定的函数进行检测校验。怎么进行具体校验的呢?
以login_required为例:
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
"""
Decorator for views that checks that the user is logged in, redirecting
to the log-in page if necessary.
"""
actual_decorator = user_passes_test(
lambda u: u.is_authenticated(),
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
login_required实现校验用户是否登录,只要是校验必须有校验的一套规则呀,也就是校验时使用的判定条件。
代码中的
user_passes_test(
lambda u: u.is_authenticated(),
# 判断条件:使用匿名函数进行校验用户是否是登录的,如果是True,就跳转redirect_field_name; 如果是False,就跳转
# login_url指定的页面。
login_url=login_url, # 如果验证不通过,进行登录页面的urlredirect_field_name=redirect_field_name # 验证通过,跳转的url
# 其实上面两个参数,就是判断后的两个分歧
)
三、上面的原理已经简介啦
怎么实现一个自定义的装饰器呢?
需求:一个公司的OA系统,需要自定义管理员模块,不再使用django提供或第三方提供的admin模块。那么,怎么实现普通用户无法访问管理员的控制台的页面呢?
需要对用户的身份进行验证,管理员进行访问,普通用户一边凉快去。
# AdminRequiredMixin 是自定义的Mixin
class AdminIndexView(AdminRequiredMixin, View):
"""实现管理员验证,只有管理员身份才能访问"""
def get(self, request):
return render(request, "admin_index.html")
那么AdminRequiredMixin是怎么实现的呢?
# 管理员身份认证 admin_required是自定义的装饰器
class AdminRequiredMixin(object):
@classmethod
def as_view(cls, **initkwargs):
view = super(AdminRequiredMixin, cls).as_view(**initkwargs)
return admin_required(view)
admin_required的定义:
from django.contrib.auth.decorators import user_passes_test
# 定义装饰器,
def admin_required(function=None, common_user_url="/user/index"):
actual_decorator = user_passes_test(
lambda u: u.is_authenticated() and u.is_superuser, # 验证条件
login_url=common_user_url, # 不满足条件,跳转路径。不要被login_url语言意义迷惑,它的逻辑其实就判断条件False时的跳转路径
redirect_field_name=None # 满足条件
)
if function:
return actual_decorator(function)
return actual_decorator
注意:login_url =common_user_url, # 不满足条件,跳转路径。
不要被login_url语言意义迷惑,它的逻辑其实就判断条件(lambda u: u.is_authenticated() and u.is_superuser)为False时的跳转路径。因为源代码实现的逻辑一般都是不满足判断条件,那就重新登录吧。所以把参数名命名为login_url 。而我们的逻辑是普通用户不能访问管理员页面,一旦访问跳转到用户主页面或者是其他提示性页面(如:提醒“非管理员权限不得访问”等),所以依旧把common_user_url 传递给login_url.