Day 30 最後的收尾

前言

今天是这个系列的最後一篇,我们会把之前没有做的东西补起来,他们都是蛮麻烦有点 tricky 的东西。

admin_required

之前我们说过,在 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 的原始码。

AddUserForm

加入一个表单并不是一件很难的事,在之前我们都做过了好多次,但这次情况有点不太一样,因为我们要在同一个页面 (管理使用者的页面) 放两个表单,这会让 form.validate_on_submit 变得有点奇怪。

我们要先稍微修改一下 AddUserFormUserFilterForm 的栏位,他们要各多加一个小栏位。

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,然後在下面判断要用哪一个表单。

结语

这系列的文章就到这边告一段落,希望不管是跟着铁人赛期间一篇一篇看的人,或是在查资料偶然翻到这个系列的人都可以有一些收获,也谢谢你们愿意阅读这篇文章。


<<:  Day-14 传值与传址

>>:  Day 15 Models介绍

Day 09:「啊~不要碰我!我会变色~」- 变化模式 (Variants)

欸欸欸!别误会啊! 可别读完标题就跑掉了。 「可是兔兔,你那个标题不妥吧!」 齁,我才是觉得你想的...

Rust-定义Closure(闭包)

一般来说Rust如果要排序数组会这样写 let mut arr = [10, 5, 9, 7, 6]...

Day25-memo

前言 前面我们学习很多关於React生命周期、状态、取得DOM元素等等,今天我们要来改善React本...

Day 22 HTML5 <HTML5 input类型、表单标签>

HTML5新增的input类型: <!-- 需添加form表单域 --> <for...

Day16-"与字串相关的函式-2"

复制字串 i.strcpy() 宣告时宣告另一空字元字串,当strcpy()执行完毕时,就会将此字...