全栈工程师开发手册 (作者:栾鹏)
python教程全解

官方:https://flask-appbuilder.readthedocs.io/en/latest/cli.html#create-app-create-new-applications

安装

pip install flask-appbuilder

命令行启动

fabmanager 命令不再使用,新版本使用Flask cli集成的新命令行管理器。不过命令形式和参数是一样的。也就是使用flask fab代替原来命令中的fabmanager

mac os先要安装证书的包

cd "/Applications/Python 3.6/"
sudo "./Install Certificates.command"

开始使用命令行flask fab的参数如下(可以通过flask fab --help 查看)

babel-compile-Babel,编译所有翻译
babel-extract-Babel,提取和更新所有消息。
create-admin-创建一个管理员用户
create-user-创建具有任意角色的用户
create-app-创建一个骨架应用程序(SQLAlchemy或MongoEngine)。
create-addon-创建骨架插件。
create-db-创建所有数据库对象(仅适用于SQLAlchemy)
collect-static-将静态文件从flask-appbuilder复制到您的静态文件夹。很高兴在某些部署上
list-users-列出数据库中的所有用户。
list-views-列出所有注册的视图。
reset-password-重置用户密码。
security-cleanup -从视图和角色清除未使用的权限。安全
security-converge-融合来自所有角色的所有安全性视图和权限名称。安全
upgrade-db-在FAB升级后升级数据库。
version -Flask-AppBuilder软件包版本。

使用命令行快速生成项目结构

flask fab create-app

创建admin用户

cd myapp
flask fab create-admin     # 会在ab_user中添加用户并绑定权限

现在,我们已经有一个基本的web后台了,下面我们来运行一下,进行查看效果。

启动

flask run --with-threads --reload    # 如果使用 flask run 命令启动,将忽视.py文件里配置8080,而采用默认的5000端口
或者
python run.py

之后我们在浏览器上输入127.0.0.1:8080就可以看到生成的页面。
在这里插入图片描述
model 修改后需要 升级数据库

flask db migrate
flask db update

config.py

config.py文件时fab的公共配置。
配置参考官网:https://flask-appbuilder.readthedocs.io/en/latest/config.html

其中系统配置

app的名字
APP_NAME = "My App Name"

# 配置连接数据库
配置SQLALCHEMY_DATABASE_URI的值来指定数据库连接。如果使用Mongdb可以配置MONGODB_SETTINGS的值。默认使用Sqlite数据库,SQLALCHEMY_DATABASE_URI的值为'sqlite:///' + os.path.join(basedir, 'app.db')。
设置为mysql
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:admin@localhost:3306/myapp'

# 通过配置AUTH_TYPE来指定应用使用的认证方式。
AUTH_TYPE = 0 | 1 | 2 | 3 | 4或AUTH_TYPE = AUTH_OID, AUTH_DB,AUTH_LDAP, AUTH_REMOTE AUTH_OAUTH。默认使用AUTH_DB的认证方式。

# 配置主题风格
Flask-AppBuilder集成了bootwatch,只需要配置APP_THEME的值就可以改变应用的主题风格。

配置主题风格
APP_THEME = "spacelab.css"

# 配置身份验证类型
AUTH_TYPE = AUTH_DB   #AUTH_OID,AUTH_DB,AUTH_LDAP,AUTH_REMOTE AUTH_OAUTH

配置自己的首页

我们在/app下创建文件index.py,并且输入下面内容:

 from flask_appbuilder import IndexView
class MyIndexView(IndexView):
    index_template = 'index.html'

接着在templates中index.html文件,在其中定义你想要的首页的内容。

{% extends "appbuilder/base.html" %}
{% block content %}
<div class="jumbotron">
  <div class="container">
    <h1>{{_("My App on F.A.B.")}}</h1>
    <p>{{_("My first app using F.A.B, bla, bla, bla")}}</p>
  </div>
</div>
{% endblock %}

最后修改app/__iniy__.py的内容,在AppBuilder初始化的时候,指定indexview的值

from app.index import MyIndexView
appbuilder = AppBuilder(app, db.session, indexview=MyIndexView)    # 替换原来的初始化语句

AppBuilder类可以指定数据库,模板目录,静态文件目录,静态文件url,安全管理,菜单等

添加视图/将视图添加到菜单

BaseView是视图中的基类,所有的视图都继承自它。当我们定义一个继承自BaseView的子类时,BaseView自动将我们用@exposed修饰的urls注册为Flask中的蓝图。我们可以通过BaseView来实现自定义页面,并添加到菜单上或者通过一个连接来访问它。这里需要注意,作为路由的方法一定要用@exposed修饰,如果需要保护的路由则需要额外使用@has_access修饰。 现在我们来看一个小例子,通常我们使用F.A.B.框架都使用自动生成的项目结构,这样我们只需要在app目录下views.py文件中修改代码即可。下面我们来看一个简单例子:


from flask_appbuilder import ModelView,AppBuilder,expose,BaseView,has_access

# 一个蓝图
class indexView(BaseView):
    # 相对路径的url
    route_base = '/index'   # 蓝图前缀路由
    default_view = 'hello'  # 设置进入蓝图的默认访问视图(没有设置网址的情况下)
    # 新视图,蓝图url+路由
    @expose('/hello')
    def hello(self):
        return 'Hello World'

    # 新视图,蓝图url+路由
    @expose('/message/<string:msg>')
    @has_access
    def message(self, msg):
        msg = 'Hello' + msg
        # 返回模板
        return self.render_template('hello.html',msg=msg)


# 后端添加视图,同时前端添加菜单
appbuilder.add_view(indexView,'Hello',category='My View')  # 使用默认视图
# 前端,向菜单栏添加子菜单,绑定链接
appbuilder.add_link('message',href='/index/message/user',category='My View')
# 前端,向菜单栏添加子菜单,绑定链接
appbuilder.add_link('welcome',href='/index/hello',category='My View')

# 添加一个后端视图,不添加前端菜单
# appbuilder.add_view_no_menu(indexView())

如果想渲染的html模板跟默认模板一样需要继承appbuilder/base.html

在’/app/templates/'目录中,创建一个hello.html,并添加下面的代码

{% extends "appbuilder/base.html" %}
{% block content %}
    <h1>Welcome</h1>
    <h2>Hello World</h2>
    <h3>{{ msg }}</h3>
{% endblock %}

此时我们再访问http://127.0.0.1:8080就需要登录之后才可以进行访问

自定义model/将model添加到前端视图

定义表的时候需要继承Model类。

修改/app/models.py这个文件,添加

from sqlalchemy import Column, Integer, String, ForeignKey ,Date
from flask_appbuilder.models.decorators import renders
from flask import Markup

#定义联系人分组,如家人、同学
class ContactGroup(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique = True, nullable=False)
    def __repr__(self):
        return self.name
# 定义联系人
class Contact(Model):
    id = Column(Integer, primary_key=True)
    name =  Column(String(150), unique = True, nullable=False)
    address =  Column(String(564))
    birthday = Column(Date)
    personal_phone = Column(String(20))
    personal_cellphone = Column(String(20))
    contact_group_id = Column(Integer, ForeignKey('contact_group.id'))    # 定义外键,绑定联系人所属分组
    contact_group = relationship("ContactGroup")
    def __repr__(self):
        return self.name

    # 自定义一个函数字段和渲染样式,供前端显示
    @renders('name')
    def my_name(self):
        return Markup('<b style="color:red">' + self.name + '</b>')

为model生成前端视图

通过继承ModelView类可以实现我们自定义的视图,F.A.B.可以针对我们定义好的数据库模型生成创建、删除、更新和显示的功能。

每个model视图,在前端会包含添加新记录list查看单条详情修改几个界面。我们可以配置下面几个参数来实现配置这几个界面。

  • label_columns:用于定义model的列在前端显示时的别名(因为model的列一般写成英文,在前端显示时我们一般设置为中文别名)
  • list_columns:用于定义list页面中要显示的字段。
  • show_fieldsets:用于定义查看记录详情页面中显示的内容,还可以单独定义 add_fieldsets 和 edit_fieldsets页面中的内容。

编辑 /app/views.py 文件

# 将model添加成视图,并控制在前端的显示
from .models import Contact,ContactGroup
from flask_appbuilder.actions import action
from flask import redirect
from flask_appbuilder.models.sqla.filters import FilterEqualFunction, FilterStartsWith,FilterEqual,FilterNotEqual
from wtforms.validators import EqualTo,Length
from flask_babel import lazy_gettext,gettext
from flask_appbuilder.security.decorators import has_access

# 定义数据库视图
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)
    # 定义创建页面要填写的字段
    add_fieldsets=[
        (
            'Summary',
            {'fields': ['name','personal_phone','contact_group']}
        )
    ]
    # 定义编辑页面要填写的字段
    edit_fieldsets = [
        (
            'Summary',
            {'fields': ['name', 'address','birthday','personal_phone','personal_cellphone', 'contact_group']}
        )
    ]
    # 定义在前端显示时,model的列,显示成一个新别名
    label_columns = {'name':'姓名','my_name':"姓名",'contact_group':'分组'}
    #定义前端model list页面显示的列。my_name为自定义样式的一列
    list_columns = ['my_name','personal_cellphone','birthday','contact_group']
    # 定义单条model记录详情显示的列
    show_fieldsets = [
        (
            'Summary',
            {'fields':['name','address','contact_group']}
        ),
        (
            'Personal Info',
            {'fields':['birthday','personal_phone','personal_cellphone'],'expanded':False}
        ),
    ]
        
    # 定义list页面的默认筛选条件的配置
    base_filters = [['name', FilterNotEqual, ''],]   # 筛选出名称为''的
    # 定义list页面的排序方法
    base_order = ('id', 'dasc')
    # 使用自定义模板配置详情页面
    # extra_args = {'name': 'SOMEVALUE'}
    # show_template = 'my_show_template.html'
    # 自定义add/update页面时表单提交自动校验
    validators_columns = {
        'personal_phone': [Length(min=11,max=11, message=gettext('fields length mush 11'))]  # message 为错误时的提示消息
    }
    # 为关联字段做自定义查询过滤器。add_form_quey_rel_fields、edit_form_query_rel_fields、search_form_query_rel_fields
    add_form_query_rel_fields = {'contact_group': [['name', FilterStartsWith, '家']]}   # 仅能选择 name字段的值以'家'开头的contact_group。
    # 自定义 页面模板
    # show_template = 'appbuilder/general/model/show_cascade.html'
    # edit_template = 'appbuilder/general/model/edit_cascade.html'
    

# 在联系人组视图中,我们使用related_views来关联联系人视图,F.A.B.将自动处理他们之间的关系。包含列表显示列,修改列
class GroupModelView(ModelView):
    datamodel = SQLAInterface(ContactGroup)
    related_views = [ContactModelView]
    base_permissions = ['can_add','can_edit', 'can_delete','can_list','can_show']   # 默认为这些

    # 自定义model视图中的操作按钮。会在数据库添加权限,和视图-权限绑定。
    # 默认在model的list页面以list形式调用该函数,在详情页面以单记录形式调用该函数。single=False固定仅在list页面显示该按钮。
    @action("muldelete", "Delete", "Delete all Really?", "fa-rocket", single=False)
    @has_access  # 为方法启动权限保护
    def muldelete(self, items):
        if isinstance(items, list):
            self.datamodel.delete_all(items)
            self.update_redirect()
        else:
            self.datamodel.delete(items)
        return redirect(self.get_redirect())
        
# 首先使用db.create_all()根据数据库模型创建表,然后再将视图添加到菜单。
db.create_all()
appbuilder.add_view(GroupModelView,"List Groups",icon = 'fa-address-book-o',category = 'Contacts',category_icon = 'fa-envelope')
appbuilder.add_separator("Contacts")   # 在指定菜单栏下面的每个子菜单中间添加一个分割线的显示。
appbuilder.add_view(ContactModelView,'List Contacts',icon = 'fa-address-card-o',category = 'Contacts')

添加后台api

在view.py中添加自定义的后端接口


# 添加自定义后端接口
from flask import request
from flask_appbuilder.api import BaseApi, expose, rison, safe   # rison 是和json类似更简单的map数据格式
from flask_appbuilder.security.decorators import protect,permission_name
from . import appbuilder

# BaseApi 类包含了所有的公开方法
class ExampleApi(BaseApi):
    resource_name='example'
    version='v1'
    base_permissions=['can_get']   # 该视图类会自动添加的权限绑定
    class_permission_name=['ExampleApi']   # 该视图类被添加为视图时的名称
    # route_base = '/newapi/v2/nice'  # 覆盖基础路由/api/{version}/{resource_name},默认是api/v1/$classname_lower
    # expose 注册为蓝图,url为http://localhost:5000/newapi/v2/nice/greeting
    @expose('/greeting', methods=['POST', 'GET'])
    def greeting(self):
        if request.method == 'GET':
            return self.response(200, message="Hello (GET)")
        return self.response(201, message="Hello (POST)")

    # 编写受权限控制的视图
    # 访问该接口,需要携带token. -H "Authorization: Bearer $TOKEN".
    # token获取:
    # curl -XPOST 'http://localhost:8080/api/v1/security/login'
    # -d '{"username": "root", "password": "admin", "provider": "db"}'
    # -H "Content-Type: application/json"
    @expose('/private')
    @protect()    # 为api启动权限保护。 会在数据库中新建一个can_rison_json的权限,并在视图上建一个这样的视图-权限绑定
    @permission_name('my_Permission')   # 自定义权限名称,而不使用默认的函数名为权限名
    def rison_json(self):
        return self.response(200, message="This is private")


    @expose('/error')
    @safe  # 使用safe装饰器正确处理所有可能的异常,它将为您捕获所有未捕获的异常并返回正确的错误响应
    def error(self):
        raise Exception


appbuilder.add_api(ExampleApi)

为model 添加rest api接口

from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_appbuilder.api import ModelRestApi
from . import appbuilder
class GroupModelApi(ModelRestApi):
    resource_name = 'group'
    datamodel = SQLAInterface(ContactGroup)
    # 该api自动生成,下面的api和绑定权限,需要有此权限的角色才能调用api
    # /_info    get                                    can_info
    # /         get,post创建记录                        can_get,can_post,can_put
    # /<id>     get读取记录,delete删除记录,put修改记录     can_get,can_delete


appbuilder.add_api(GroupModelApi)

chart 视图的使用

BaseChartView为谷歌图表,前端页面显示时需要连接谷歌。
DirectChartView,GroupByChartView,ChartView,TimeChartView 均继承自BaseChartView

定义一个model

# charts视图的model
from sqlalchemy import Column, Integer, String, ForeignKey,Float
import datetime

# insert into country (name) values ('china')
# insert into country_stats (stat_date,population,unemployed_perc,poor_perc,college,country_id) values ('2019-10-11',100,10,10,30,1), ('2019-11-11',110,9,10,30,1), ('2019-12-11',120,8,10,30,1)


class Country(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique = True, nullable=False)

    def __repr__(self):
        return self.name

class CountryStats(Model):
    id = Column(Integer, primary_key=True)    # 国家历史状态id
    stat_date = Column(Date, nullable=True)   # 当时的时间
    population = Column(Float)                # 当时的人口总数
    unemployed_perc = Column(Float)           # 失业人口比例
    poor_perc = Column(Float)    # 当时的贫困者比例
    college = Column(Float)    # 当时的大学生总数
    country_id = Column(Integer, ForeignKey('country.id'), nullable=False)    # 国家id
    country = relationship("Country")

    # 定义函数计算全国失业人数/全国大学生
    def college_perc(self):   # 函数可以作为一个字段对后端接口使用,像查询列一样获取数据
        if self.population != 0:
            return (self.college * 100) / self.population
        else:
            return 0.0
    # 定义时间新字段
    def month_year(self):
        return datetime.datetime(self.stat_date.year, self.stat_date.month, 1)

DirectChartView

添加视图代码

from flask_appbuilder.charts.views import DirectByChartView
from flask_appbuilder.models.sqla.interface import SQLAInterface

from .models import CountryStats

class CountryDirectChartView(DirectByChartView):
    datamodel = SQLAInterface(CountryStats)
    # 每个chart包含过滤(查询数据源)和报表可视化显示两部分
    chart_title = 'Direct Data Example'
    chart_type = 'ColumnChart'  # 图表类型 PieChart,ColumnChart或LineChart
    # 定义报表部分前端页面的相关显示。多个图表,每个图表包含多个曲线数据。
    definitions = [
        {
            'label': 'Unemployment',    # 当前图表的标题
            'group': 'stat_date',       # 用来作为x的字段。
            'series': ['unemployed_perc','college_perc']  # 用来作为y的字段,包含多个y值
        },

        {
            'label': 'Poor',
            'group': 'stat_date',
            'series': ['poor_perc','college_perc']
        }
    ]

appbuilder.add_view(CountryDirectChartView, "Show Country Chart", icon="fa-dashboard", category="Statistics")

在这里插入图片描述

分组图表的使用


#  分组图表的使用
from flask_appbuilder.charts.views import GroupByChartView
from flask_appbuilder.models.group import aggregate_count, aggregate_sum, aggregate_avg   # 分组计算函数
from flask_appbuilder.models.sqla.interface import SQLAInterface
import calendar
from .models import CountryStats
from .models import Country
class CountryGroupByChartView(GroupByChartView):
    datamodel = SQLAInterface(CountryStats)
    chart_title = 'Statistics'

    definitions = [
        {
            'label': 'Country Stat',
            'group': 'month_year',    # x的取值字段
            'formatter': lambda value:calendar.month_name[value.month] + ' ' + str(value.year),   # x值的显示格式
            'series': [(aggregate_avg, 'unemployed_perc'),   # 分组后,指定字段值集合进行计算后再显示
                       (aggregate_avg, 'population'),
                       (aggregate_avg, 'college_perc')
                      ]
        }
    ]

appbuilder.add_view(CountryGroupByChartView, "Show group Country Chart", icon="fa-dashboard", category="Statistics")

在这里插入图片描述

后端代码对数据库的影响

  • 在后端添加的add_api的viewclass/add_view中的viewclass,name/add_link中的name,category 等都会在ab_view_menu表中中添加为视图,注意并不是每个视图函数或网址接口都添加为视图。
  • @protect()@has_access修饰的视图函数,或在ab_permission中生成can_viewname的权限,并且创建在该视图上的该权限的绑定。只有有此权限绑定的角色才能调用这个视图。

自定义模板

您可以添加自己的js,css,导航栏模板,list,add,edit,show,edit-related级联页面模板,参考:https://flask-appbuilder.readthedocs.io/en/latest/templates.html#css-and-javascript

appbuilder自带的安全权限

支持的身份验证方式

  • Database验证:从数据库查询要匹配的用户名和密码样式。密码在数据库中保持散列。
  • Open ID方式验证:使用用户的电子邮件字段在Gmail,Yahoo等上进行身份验证。使用Flask-Login保留并加密会话,OpenID需要Flask-OpenID
  • LDAP方式验证:针对LDAP服务器(例如Microsoft Active Directory)的身份验证
  • REMOTE_USER方式:读取REMOTE_USER Web服务器environ var,并验证它是否已获得框架用户表的授权。当服务器(Apache,Nginx)配置为使用kerberos时,Web服务器负责对用户进行身份验证,这对于Intranet站点很有用,无需用户在FAB上使用用户名和密码登录
  • OAUTH方式验证:使用OAUTH(v1或v2)进行身份验证。您需要安装flask-oauthlib。三方服务器授权层,授权Flask AppBuilder一个token,Flask AppBuilder使用这个token获取三方服务器上的数据。
# 在配置文件中配置身份验证类型
AUTH_TYPE = AUTH_DB   #AUTH_OID,AUTH_DB,AUTH_LDAP,AUTH_REMOTE AUTH_OAUTH

每种认证方式的配置参考:https://flask-appbuilder.readthedocs.io/en/latest/security.html#authentication-methods

自定义身份认证方式

由于user不仅牵扯到认证授权,还有model的增删改查,所以如果我们自定义认证方式,需要和系统自带的user-model联系在一起,既能保证自定义认证,又能保证原有的user在数据库中的增删改查。

认证和user-model是两个模块组成的。

在config.py文件中添加

# 自定义认证方式的user model。继承usermodel,因为也是需要在数据库中存储和查询的。
class MyUserDBView(UserDBModelView):
    @action("muldelete", "Delete", "Delete all Really?", "fa-rocket", single=False)
    def muldelete(self, items):
        self.datamodel.delete_all(items)
        self.update_redirect()
        return redirect(self.get_redirect())

# 自定义认证策略
class MySecurityManager(SecurityManager):
    userdbmodelview = MyUserDBView


FAB_SECURITY_MANAGER_CLASS=MySecurityManager

FAB为每种身份验证方法使用不同的用户视图

  • UserDBModelView:对于数据库身份验证方法
  • UserOIDModelView:对于开放ID身份验证方法
  • UserLDAPModelView:对于LDAP身份验证方法

当然我们可以扩展系统自带的user model

  • AUTH_DB: 扩展UserDBModelView
  • AUTH_LDAP: 扩展UserLDAPModelView
  • AUTH_REMOTE_USER:扩展UserRemoteUserModelView
  • AUTH_OID: 扩展UserOIDModelView
  • AUTH_OAUTH: 扩展UserOAuthModelView

一种方式是直接在config.py文件中


# 自定义扩展系统自带的user
class MyUser(User):
    __tablename__ = 'ab_user'
    extra = Column(String(256))   # 新增的属性

# 自定义认证方式的user model。继承usermodel,因为也是需要在数据库中存储和查询的。
class MyUserDBView(UserDBModelView):
    @action("muldelete", "Delete", "Delete all Really?", "fa-rocket", single=False)
    def muldelete(self, items):
        self.datamodel.delete_all(items)
        self.update_redirect()
        return redirect(self.get_redirect())

# 自定义认证策略
class MySecurityManager(SecurityManager):
    user_model = MyUser
    userdbmodelview = MyUserDBView


FAB_SECURITY_MANAGER_CLASS=MySecurityManager

一种方式是单独定义扩展model的py文件,单独定义扩展view的文件,然后在__init__.py文件中添加自定义认证方法

from flask_appbuilder.security.sqla.manager import SecurityManager
from .sec_models import MyUser
from .sec_views import MyUserDBModelView

class MySecurityManager(SecurityManager):
    user_model = MyUser
    userdbmodelview = MyUserDBModelView

# appbuilder = AppBuilder(app, db.session, indexview=MyIndexView,menu=Menu(reverse=False),base_template='mybase.html',security_manager_class=MySecurityManager)

Flask AppBuilder自带的所有视图类

在这里插入图片描述

  • BaseView: 收集所有公开的方法,创建Flask蓝图并注册URL,初始化基本权限。
  • UtilView: 实现公开的背面以实现特殊的背面UI功能。
  • IndexView: 呈现index页面的特殊视图。
  • SimpleFormView: 对其进行子类化以呈现WTForms。
  • PublicFormView: 与SimpleFormView相同,但仅具有公共访问权限。
  • BaseModelView: 负责初始数据模型层的类,实现搜索表单和过滤器。
  • BaseChartView: 基本图表视图功能。
  • GroupByChartView:对其进行子类化,以按查询分组显示Google图表。
  • DirectByChartView:对其进行子类化以呈现带有查询的Google图表。
  • BaseCRUDView: 实现基本功能以添加,编辑,删除,创建所有表单。
  • RestCRUDView: 公开用于CRUD方法的JSON REST API等。
  • ModelView: 对其进行子类化,以基于模型呈现您的视图,并具有完整的CRUD UI功能。
  • MasterDetailView:渲染与数据库有关一个主模型视图和多个细节模型视图。
  • MultipleView: 在同一页面上呈现多个视图(例如:ModelView和GroupByChartView)

Flask AppBuilder自带的用于数据库访问的类

在这里插入图片描述

  • BaseInterface: 接口类,为数据访问强加了唯一的API层。
  • SQLAInterface: SQLAlchemy的数据访问。
  • MongoEngineInterface:MongoEngine(MongoDB)的数据访问。
  • GenericInterface: 自定义数据结构的数据访问。

Flask AppBuilder 中 Security相关的类

在这里插入图片描述

  • BaseManager: 所有Manager类的基类都包含AppBuilder类。
  • AbstractSecurityManager:安全管理器的抽象类,定义必须具有的方法。
  • BaseSecurityManager:安全性的基类,注册安全性视图,实现身份验证,插入/删除数据库上的所有权限,管理角色/用户和视图。
  • sqla.SecurityManager:为SQAlchemy实现BaseSecurityManager。
  • mongoengine.SecurityManager:为MongoEngine实现BaseSecurityManager。

用户、角色、权限、视图之间的关系

在这里插入图片描述

每个用户可能具有多个角色,并且一个角色拥有对视图/ API和菜单的权限,因此产生了用户对视图/ API和菜单具有权限。

配置文件中配置自带的角色

格式为
FAB_ROLES = {
    "<ROLE NAME>": [
        ["<VIEW/MENU/API NAME>", "PERMISSION NAME"],
        ....
    ],
    ...
}

示例
# 添加自带角色和具有的视图-权限
FAB_ROLES = {
    "ReadOnly": [
        [".*", "can_list"],
        [".*", "can_show"],
        [".*", "menu_access"],
        [".*", "can_get"],
        [".*", "can_info"]
    ]
}
# 修改之前的角色id,为新的角色名称。自带Admin和Public不能替换名称。
# FAB_ROLES_MAPPING = {
#     3: "role1"
# }

权限

Flask AppBuilder 自动为视图、API或菜单,创建所有可能的现有权限。

每次基于模型(从ModelView继承)创建新视图时,都会创建以下权限:

  • can list
  • can show
  • can add
  • can edit
  • can delete
  • can download

对于CRUD REST API:

  • can get
  • can put
  • can post
  • can delete
  • can info

除了创建权限,还会创建在这些自定义视图上的权限绑定记录。因为权限都是在视图上才有意义。

如果我们想在视图类上创建访问视图函数的权限。使用@has_access修饰我们的视图函数(在api类上使用@protect()修饰)。那么框架将根据您的视图函数名称创建权限,并添加MyModelView上的mymethod,这个视图-权限绑定对。

可以使用@permission_name自定义权限名称,而采用默认的视图函数名作为权限名称。

上面的权限过于精细化,当视图过多时非常麻烦,可以将权限进行合并。

  • 使用class_permission_name直接对一个类里面的所有方法统一设置权限。将多个方法合并设置
  • 使用method_permission_name对多种权限统一设定。将多种权限统一设定
class OneApi(ModelRestApi):
    datamodel = SQLAInterface(Contact)
    class_permission_name = "api"
    method_permission_name = {
        "get_list": "access",
        "get": "access",
        "post": "access",
        "put": "access",
        "delete": "access",
        "info": "access"
    }
    # 修改之前的权限名称。如果之前的名称是get,就换成最新的access
    previous_method_permission_name = {
        "get_list": "get",
        "get": "get",
        "post": "get",
        "put": "get",
        "delete": "get",
        "info": "get"
    }

获取的视图和权限为
can access on api

清理之前的垃圾视图和权限

# 修改代码后,视图/权限名称变更,会在数据库中生成新的记录,旧记录删除需要下面的语句。
appbuilder.security_cleanup()
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐