今天是這個系列的最後一篇,我們會把之前沒有做的東西補起來,他們都是蠻麻煩有點 tricky 的東西。
之前我們說過,在 admin_bp
裡面的路徑沒有辦法用 login_required
,因為他只會驗證使用者是否有登入,但不會判斷該使用者是否為管理員。所以我們需要自己寫一個 admin_required
。
我們要回到 app/user_helper.py
,然後對 login_manager
做一點點的修改。
from functools import wraps
from flask import current_app, request, abort, flash, redirect, session
from flask_login import LoginManager, UserMixin, current_user
from flask_login.config import USE_SESSION_FOR_NEXT
from flask_login.signals import user_unauthorized
from flask_login.utils import (
expand_login_view,
login_url as make_login_url,
make_next_param,
)
class LoginManager_(LoginManager):
def __init__(self):
super().__init__()
def forbidden(self):
user_unauthorized.send(current_app._get_current_object())
if self.unauthorized_callback:
return self.unauthorized_callback()
if request.blueprint in self.blueprint_login_views:
login_view = self.blueprint_login_views[request.blueprint]
else:
login_view = self.login_view
if not login_view:
abort(403)
if self.login_message:
if self.localize_callback is not None:
flash(
self.localize_callback(self.login_message),
category=self.login_message_category,
)
else:
flash(self.login_message, category=self.login_message_category)
config = current_app.config
if config.get("USE_SESSION_FOR_NEXT", USE_SESSION_FOR_NEXT):
login_url = expand_login_view(login_view)
session["_id"] = self._session_identifier_generator()
session["next"] = make_next_param(login_url, request.url)
redirect_url = make_login_url(login_view)
else:
redirect_url = make_login_url(login_view, next_url=request.url)
return redirect(redirect_url)
我們重新定義了一個 LoginManager_
,並在裡面新增了 forbidden
這個函式,他是 unauthorized
的變形,基本上都是直接抄過來的,我們只把裡面的 abort
的 status code 改成 403 而已。這個 unauthorized
會在 login_required
裡面用到,所以我們在這邊加入一個 forbidden
之後,就可以在 admin_required
裡面使用了,所以我們馬上就來寫他。
def admin_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if current_user.is_active:
if current_user.is_admin:
return func(*args, **kwargs)
else:
return login_manager.forbidden()
else:
return login_manager.unauthorized()
return decorated_view
基本上它的結構也跟 login_required
很像,但我們有修改裡面的邏輯判斷,讓他可以確定他有沒有登入和是否為管理員。最後我們再把這個裝飾器加到每個 admin_bp
裡面,就可以做出 admin_required
的效果了。
這邊我們沒有多做解釋,如果有興趣的話可以去看 Flask-Login 的原始碼。
加入一個表單並不是一件很難的事,在之前我們都做過了好多次,但這次情況有點不太一樣,因為我們要在同一個頁面 (管理使用者的頁面) 放兩個表單,這會讓 form.validate_on_submit
變得有點奇怪。
我們要先稍微修改一下 AddUserForm
和 UserFilterForm
的欄位,他們要各多加一個小欄位。
forms.py
from wtforms import HiddenField
class AddUserForm(FlaskForm):
form_name = HiddenField(render_kw={"value": "add_one"})
class UserFilterForm(FlaskForm):
form_name = HiddenField(render_kw={"value": "filter"})
我們各加了一個 HiddenField
,我們等等會用這個欄位來判斷現在使用的是哪一個表單。以下的程式碼只有在 POST 底下的部分,form
還是原來在用的 UserFilterForm
,而 form_add_user
則是 AddUserForm
。接下來我們抓 request.form["form_name"]
也就是剛剛我們放進去的 HiddenField
,然後在下面判斷要用哪一個表單。
這系列的文章就到這邊告一段落,希望不管是跟著鐵人賽期間一篇一篇看的人,或是在查資料偶然翻到這個系列的人都可以有一些收穫,也謝謝你們願意閱讀這篇文章。