django2.0記錄 使用登錄裝飾器實現訪問限制及類視圖使用裝飾器
背景介紹
python3環境,前後端不分離,前端使用bootstrap2框架,後端使用django2.0框架,包含登錄前端代碼以及後端實現,只是個人日常記錄,僅供參考
目的
訪問未登錄的視圖函數,需要先跳轉到登錄頁面,登陸成功在跳轉回來
實現思路
跳轉到登錄頁面很容易,就是登陸成功跳轉回來出了問題,原因是登錄後的post請求裏拿不到之前url中next的值,所以只需要在發起get請求時,將next的值傳遞到模板的input標籤裏,type爲hidden,之後再用post請求拿回來,就可以了!
前端代碼
<div class="row-fluid">
<div class="login-box">
<h2 style="text-align: center">後臺管理系統</h2>
<form class="form-horizontal" action="/login/" method="post">
{% csrf_token %}
<fieldset>
<div class="input-prepend" title="Username">
<span class="add-on"><i class="halflings-icon user"></i></span>
<input class="input-large span10" name="username" id="username" type="text"
placeholder="請輸入用戶名"/>
</div>
<div class="clearfix"></div>
<div style="padding-bottom: 0" class="input-prepend" title="Password">
<span class="add-on"><i class="halflings-icon lock"></i></span>
<input class="input-large span10" name="password" id="password" type="password"
placeholder="請輸入密碼"/>
</div>
{% if errmsg %}
<span style="color: red; padding-left: 120px">{{ errmsg }}</span>
{% endif %}
<input type="hidden" name="nexturl" value="{{ nexturl }}">
<div class="clearfix"></div>
<div class="button-login">
<button style="text-align: center; width: 356px" type="submit" class="btn btn-primary">登陸
</button>
</div>
<div class="clearfix"></div>
</fieldset>
</form>
<hr>
</div><!--/span-->
</div>
後端實現代碼
settings.py
LOGIN_URL = '/login/' # 配置未登錄時間登錄的跳轉地址
url.py
from django.urls import path
from django.contrib.auth.decorators import login_required
from apps.user import views
urlpatterns = [
path('login/', views.UserLoginView.as_view()),
path('loginout/', login_required(views.UserLoginOutView.as_view())),
]
login_required 添加登錄裝飾器,未登錄狀態下跳轉登錄頁面
view.py
class UserLoginView(View):
"""
用戶登錄視圖
"""
def get(self, request):
nextUrl = request.GET.get("next", "")
if request.user.is_authenticated:
if nextUrl:
return redirect(nextUrl)
else:
return HttpResponse('登陸成功')
else:
return render(request, 'login/login.html', {'nexturl': nextUrl})
def post(self, request):
# 1. 獲取參數和判斷是否有值
username = request.POST.get("username")
password = request.POST.get("password")
nextUrl = request.POST.get("nexturl", "")
if not all([username, password, code]):
# 參數不全
return render(request, 'login/login.html', {'errmsg': '參數不全'})
# 校驗用戶密碼
user = authenticate(request, username=username, password=password)
if user is not None:
# 保存用戶登錄信息
request.session["is_login"] = True
request.session["username"] = username
login(request, user)
if nextUrl:
return redirect(nextUrl)
else:
return redirect(reverse('app_name:list', ))
else:
return render(request, 'login/login.html', {'errmsg': '用戶名或者密碼錯誤'})
django爲類視圖添加登錄裝飾器的方法
第一種:直接在url上下手。
path('loginout/', login_required(views.UserLoginOutView.as_view())),
第二種:使用method_decorator裝飾器
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
class LoginRequiredMixin(object):
'''登錄狀態校驗'''
@method_decorator(login_required(login_url='/login/'))
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
說明:
method_decorator裝飾器,是django自帶的一個裝飾器,其最主要的作用就是解決裝飾器不能直接的裝飾類視圖函數(method_decorator的作用是爲函數視圖裝飾器補充第一個self參數)。
它具有兩個參數,第一個參數是:裝飾器本身及它的參數;第二個參數是:給這個類中的哪個函數裝飾。
如果你願意去看一下該裝飾器的源碼
關於dispatch()函數:在其View源碼中,它是起着請求映射分發的作用的,其實在執行as_view()的時候,內部就是在執行的就是self.dispatch()方法,你可以自己重寫一下,然後給重寫的加上裝飾器,就可以在其實施分發前進行了登錄驗證 了。
使用說明:你需要讓你的類視圖函數繼承:LoginRequiredMixin,且在View之前。
這種方法的使用還可以用於取消部分類視圖中的csrf驗證(僅列舉取消單個視圖處理類的csrf驗證,如果你需要將很多的處理類都取消csrf的話,可以使用繼承)。示例如下(也可以配合method_decorator的第二個參數,直接裝飾類):
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect
class TestView(View):
"""取消該視圖處理類中的csrf驗證"""
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(TestView, self).dispatch(self, request, *args, **kwargs)
def get(self, request):
pass
def post(self, request):
pass
第三種:利用繼承關係,實現在調用執行視圖函數前調用login__required()
from django.contrib.auth.decorators import login_required
class LoginRequiredMixin(object):
'''登錄狀態校驗'''
@classmethod
def as_view(cls, **initkwargs):
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
return login_required(view)
在url最主要的還是要實現:login_required(CommentsView.as_view())
而此時就是利用繼承優雅的實現。
使用說明:你需要讓你的類視圖函數繼承:LoginRequiredMixin,且在View之前。
補充
- login_required的跳轉函數:你可以不指定(會使用默認的),也可以在其參數中指定,也可以在settings.LOGIN_URL指定。
- 第三種和第二種的區別就在於:前者深入到分發過程中,在分發前做了驗證;後者在分發後做的驗證。