今天是这个系列的最後一篇,我们会把之前没有做的东西补起来,他们都是蛮麻烦有点 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
,然後在下面判断要用哪一个表单。
这系列的文章就到这边告一段落,希望不管是跟着铁人赛期间一篇一篇看的人,或是在查资料偶然翻到这个系列的人都可以有一些收获,也谢谢你们愿意阅读这篇文章。
欸欸欸!别误会啊! 可别读完标题就跑掉了。 「可是兔兔,你那个标题不妥吧!」 齁,我才是觉得你想的...
一般来说Rust如果要排序数组会这样写 let mut arr = [10, 5, 9, 7, 6]...
前言 前面我们学习很多关於React生命周期、状态、取得DOM元素等等,今天我们要来改善React本...
HTML5新增的input类型: <!-- 需添加form表单域 --> <for...
复制字串 i.strcpy() 宣告时宣告另一空字元字串,当strcpy()执行完毕时,就会将此字...