Django用户认证系统提供了 login_required 函数装饰器来限制页面的访问,使用 login_required 装饰器的视图函数的页面,只有登录的用户可以访问。login_required 装饰器定义在 django.contrib.auth.decorators 模块中,内部封装了 is_authenticated 来判断用户是否登录。
如果已经登录则进入内部视图,执行视图函数逻辑。
如果未登录默认被重定向到 setting.LOGIN_URL 配置项指定的地址。
1、login_required 装饰函数视图
from django.contrib.auth.decorators import login_required
@login_required(login_url=reverse('users:login'))
def userinfo_view(request):
return render(request, "user_center_info.html")
login_required 装饰器可以装饰一个视图函数(接收request参数,返回response响应的函数),如果request中的user已经登录,就返回视图函数返回response,如果request中的user是匿名用户,就返回重定向页面。
2、login_required 装饰类视图
login_required装饰器可以直接装饰函数视图,但是不能直接装饰类视图。
login_required装饰器的本质是把要装饰的函数对象传入login_required()函数中,函数返回被装饰过的函数对象,所以只要能够拿到函数视图的函数对象,就可以使用 login_required 来装饰。
类视图的as_view()方法就是将类视图转成函数视图,as_view()方法的返回值是一个函数对象,要想使用 login_required装饰器 来装饰类视图,可以间接的装饰 as_view()方法 的返回值。
接下来,来聊一聊具体怎么使用装饰器来装饰类视图。
第一种方式,as_view()方法是在url配置的时候调用的,所以可以在url配置的时候装饰as_view()方法的返回值。
# urls.py
re_path("info/$", login_required(views.UserCenterView.as_view()), name="info"),
# views.py
class UserInfoView(View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, "user_center_info.html")
第二种方式,在类继承的时候,我们可以通过super.as_view()方法调用as_view()方法,并且得到函数视图,所以我们还可以通过类继承来使用login_required装饰as_view()返回值。
# urls.py中不用login_required来装饰
re_path("info/$", views.UserCenterView.as_view(), name="info"),
这里提供三种方案的思路来实现第二种方式的逻辑。
方案一:UserInfoView里面重写as_view()方法,使用login_required装饰super.as_view()返回值
# views.py
class UserCenterView(View):
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view()
return login_required(view) # 类视图的装饰方法
def get(self, request: HttpRequest):
"""打开用户中心页面"""
return render(request, "user_center_info.html")
总结:每个需要验证用户是否登录的页面都需要重写as_view方法。
方案二:UserInfoView -> LoginRequiredView -> View,在LoginRequiredView里面重写as_view()方法,使用 login_required 装饰 super.as_view()返回值。
# views.py
class LoginRequiredView(View):
"""验证用户是否登录"""
@classmethod
def as_view(cls, *args, **kwargs):
# 自定义的as_view()方法中,调用父类的as_view()方法
view = super.as_view(*args, **kwargs)
return login_required(view)
class UserCenterView(LoginRequiredView):
"""用户中心"""
def get(self, request: HttpRequest):
"""打开用户中心页面"""
return render(request, "user_center_info.html")
总结:LoginRequiredView(object)依赖于视图类View,但我们不会单独使用LoginRequiredView来创建视图实例,只是使用LoginRequiredView来增强UserInfoView的功能,所以我们不需要LoginRequiredView类依赖于视图类View,对于这种需求,python里面有一种习惯,把这种类定义为Mixin类。
方案三:使用多继承的MRO(方法解析顺序)特征。
class LoginRequiredMixin(object):
@classmethod
def as_view(self, *args, **kwargs):
view = super().as_view()
return login_required(view)
class UserCenterView(LoginRequiredMixin, View): # mixin设计模式
def get(self, request: HttpRequest):
"""打开用户中心页面"""
context = {
"username": request.user.username,
"mobile": request.user.mobile,
"email": request.user.email,
"email_active": request.user.email_active,
}
return render(request, "user_center_info.html", context)