superset二次开发学习笔记day 1
Day 1:从 Superset 启动和 init_views 入手
本文是基于 Superset 5.0 源码阅读过程整理的学习笔记。
Day 1 主要目标:先搞清楚 Superset 的启动入口,以及 API、页面、菜单是如何被注册到系统中的。
1. Superset 本质是 Flask 应用
Superset 的后端本质上是一个 Flask 应用。
它的启动入口大致是:
create_app()
启动时会创建 Flask app,加载配置,然后调用初始化器:
SupersetAppInitializer(app).init_app()
真正大量初始化逻辑在:
superset/initialization/__init__.py
初始化过程大致包括:
加载配置
初始化日志
初始化 Feature Flag
初始化数据库
初始化 Celery
初始化缓存
初始化 Flask-AppBuilder
注册 API / View / Menu
其中二次开发最值得先看的方法是:
init_views()
不过需要注意:
init_views() 不是 Superset 的全部启动流程。
它只负责 API、页面 View、菜单入口的注册。
数据库、缓存、Celery、权限管理器等初始化在 init_app() 的其他步骤中完成。
也就是说,init_views() 是理解 Superset 后端模块注册的入口,但不是整个系统启动的全部逻辑。
2. init_views 是 API / 页面 / 菜单注册中心
init_views() 主要负责把 Superset 的 API、页面 View、菜单入口注册到 Flask-AppBuilder 中。
常见注册方式有四种:
appbuilder.add_api(...)
appbuilder.add_view(...)
appbuilder.add_link(...)
appbuilder.add_view_no_menu(...)
含义如下:
| 方法 | 作用 |
|---|---|
add_api | 注册 REST API |
add_view | 注册页面 View,并可能加入菜单 |
add_link | 注册菜单链接 |
add_view_no_menu | 注册页面,但不显示在菜单中 |
例如:
appbuilder.add_api(DatabaseRestApi)
appbuilder.add_api(DashboardRestApi)
appbuilder.add_api(ChartRestApi)
appbuilder.add_api(SqlLabRestApi)
这些是注册数据库、仪表盘、图表、SQL Lab 相关 API。
再比如:
appbuilder.add_view(
DatabaseView,
"Databases",
label=__("Database Connections"),
icon="fa-database",
category="Data",
category_label=__("Data"),
)
这表示注册数据库连接管理页面,对应页面入口大致是:
Settings -> Data -> Database Connections
所以可以把 init_views() 理解为:
Superset 后端 API、页面、菜单的集中注册位置。
3. add_api、add_view、add_link、add_view_no_menu 的区别
3.1 add_api
add_api 用来注册 REST API。
例如:
appbuilder.add_api(DatabaseRestApi)
注册后,前端就可以通过类似下面的接口访问数据库连接相关 API:
/api/v1/database/
常见 API 有:
DatabaseRestApi
DatasetRestApi
ChartRestApi
DashboardRestApi
SqlLabRestApi
SecurityRestApi
这些 API 主要负责处理前端发来的 REST 请求,比如查询列表、新增、修改、删除、执行 SQL 等。
3.2 add_view
add_view 用来注册页面 View,并可能把它挂到菜单中。
例如:
appbuilder.add_view(
DatabaseView,
"Databases",
label=__("Database Connections"),
icon="fa-database",
category="Data",
category_label=__("Data"),
)
这会注册一个数据库连接管理页面。
页面本身通常只是返回 Superset 的前端 React 页面壳子,真正的数据获取还是通过 REST API 完成。
例如:
打开 /databaseview/list/
↓
返回 React 页面
↓
前端再请求 /api/v1/database/ 获取数据库连接列表
3.3 add_link
add_link 用来注册菜单链接。
例如:
appbuilder.add_link(
"SQL Editor",
label=__("SQL Lab"),
href="/sqllab/",
category="SQL Lab",
category_label=__("SQL"),
)
它不会像 add_view 那样注册一个完整 View 类,而是在菜单中增加一个跳转入口。
点击后会跳到:
/sqllab/
3.4 add_view_no_menu
add_view_no_menu 用来注册页面,但不显示在菜单中。
例如:
appbuilder.add_view_no_menu(ExploreView)
appbuilder.add_view_no_menu(SqllabView)
appbuilder.add_view_no_menu(Superset)
这些页面可以被访问,但不会直接出现在菜单里。
比如 Explore 页面通常是从 Chart 跳过去的,不一定需要一个单独菜单入口。
可以理解为:
add_view:
注册页面,并可能显示到菜单
add_view_no_menu:
注册页面,但不显示菜单
4. DatabaseView、Databases、Database Connections 的区别
以这段代码为例:
appbuilder.add_view(
DatabaseView,
"Databases",
label=__("Database Connections"),
icon="fa-database",
category="Data",
category_label=__("Data"),
)
里面有几个名字很容易混:
| 名称 | 含义 |
|---|---|
DatabaseView | Python 类名 |
"Databases" | FAB 内部注册名 |
"Database Connections" | 菜单展示名称 |
category="Data" | 菜单分组 |
class_permission_name = "Database" | 权限系统里的权限对象名 |
所以可以这样理解:
DatabaseView 是代码里的类
Databases 是 FAB 注册名
Database Connections 是用户看到的菜单名
Database 是权限系统里的名字
这几个名字虽然相关,但作用不同。
5. URL 是怎么生成的
以 DatabaseView 为例:
class DatabaseView(BaseSupersetView):
@expose("/list/")
@has_access
def list(self):
return super().render_app_template()
如果一个 View 没有显式设置 route_base,Flask-AppBuilder 默认会根据类名生成 URL 前缀:
DatabaseView -> databaseview
再拼上方法上的路由:
@expose("/list/")
最终得到:
/databaseview/list/
所以 Database Connections 页面一般对应:
/databaseview/list/
这里需要注意,URL 不是由:
"Databases"
或者:
label="Database Connections"
生成的,而是主要由:
View 类名
+
@expose 路径
决定的。
6. route_base 的作用
如果某个 View 类中显式定义了 route_base,则会优先使用 route_base。
例如:
class TagModelView(SupersetModelView):
route_base = "/superset/tags"
那么它的访问路径就会基于:
/superset/tags
而不是默认的:
/tagmodelview
所以 URL 生成规则可以总结为:
默认情况:
类名小写 + @expose 路径
自定义情况:
route_base + @expose 路径
7. DatabaseView 页面本身做了什么
DatabaseView 类大概类似:
class DatabaseView(BaseSupersetView):
class_permission_name = "Database"
method_permission_name = MODEL_VIEW_RW_METHOD_PERMISSION_MAP
@expose("/list/")
@has_access
def list(self):
return super().render_app_template()
这段代码的含义是:
class_permission_name = "Database"
表示这个 View 在权限系统里的权限对象名是 Database
method_permission_name = MODEL_VIEW_RW_METHOD_PERMISSION_MAP
表示把 list/show/add/edit/delete 等方法映射成 read/write 权限
@expose("/list/")
注册 URL 路由
@has_access
访问前检查当前用户是否有权限
render_app_template()
返回 Superset 前端 React 页面
也就是说,DatabaseView.list() 本身不一定直接查询数据库连接列表。
它主要负责:
注册页面路由
做权限校验
返回前端页面模板
真正的数据获取通常由 REST API 完成,比如:
/api/v1/database/
8. 页面 View 和 REST API 的关系
Superset 很多页面是这样的模式:
View 返回前端页面
↓
React 页面加载
↓
前端请求 REST API 获取数据
以 Database 为例:
/databaseview/list/
↓
DatabaseView.list()
↓
返回 React 页面
↓
前端请求 /api/v1/database/
↓
DatabaseRestApi 返回数据库连接列表
所以二次开发时,要区分:
页面入口:
DatabaseView
数据接口:
DatabaseRestApi
类似地:
| 页面入口 | 数据接口 |
|---|---|
DatabaseView | DatabaseRestApi |
DashboardModelView / Dashboard | DashboardRestApi |
SliceModelView | ChartRestApi |
TableModelView / Datasets link | DatasetRestApi |
SqllabView | SqlLabRestApi |
9. init_views 中值得重点看的模块
init_views() 里注册了很多模块。
二次开发时不建议一开始全部看完,可以按优先级学习。
第一优先级
DatabaseRestApi
DatasetRestApi
ChartRestApi
DashboardRestApi
SqlLabRestApi
这些是 Superset 最核心的业务模块。
第二优先级
SecurityRestApi
CurrentUserRestApi
UserRestApi
RLSRestApi
SavedQueryRestApi
这些和用户、权限、SQL 查询、行级权限相关。
第三优先级
TagRestApi
AnnotationRestApi
ReportScheduleRestApi
EmbeddedDashboardRestApi
ImportExportRestApi
LogRestApi
这些是标签、注释、报表、嵌入式看板、导入导出、日志相关功能。
10. 看一个 API 模块时应该怎么看
以:
appbuilder.add_api(DatabaseRestApi)
为例,下一步应该去看:
superset/databases/api.py
重点看这些内容:
resource_name 是什么
class_permission_name 是什么
method_permission_name 是什么
base_permissions 有哪些
@expose 注册了哪些 URL
@protect / @permission_name 控制什么权限
调用了哪些 Command
调用了哪些 DAO
操作了哪些 Model
通常一个模块的后端结构是:
API 层
↓
Command 层
↓
DAO 层
↓
Model 层
↓
Metadata DB
例如 Database 模块大致是:
DatabaseRestApi
↓
CreateDatabaseCommand / UpdateDatabaseCommand / DeleteDatabaseCommand
↓
DatabaseDAO
↓
Database Model
Chart 模块大致是:
ChartRestApi
↓
CreateChartCommand / UpdateChartCommand
↓
ChartDAO
↓
Slice Model
11. Day 1 小结
这一部分主要解决了一个问题:
Superset 的 API、页面和菜单是怎么挂载到系统里的?
整体链路可以理解为:
Superset 启动
↓
SupersetAppInitializer.init_app()
↓
init_views()
↓
appbuilder.add_api / add_view / add_link / add_view_no_menu
↓
API、页面、菜单被注册到 FAB
↓
后续由 FAB 权限体系决定用户能否看到和访问
init_views() 是阅读 Superset 二次开发源码的一个很好入口,因为它集中列出了 Superset 的主要模块:
Database
Dataset
Chart
Dashboard
SQL Lab
Security
Annotation
Tag
Report
RLS
后续学习时,可以从这里挑一个模块继续往下追,比如:
DatabaseRestApi
↓
Command
↓
DAO
↓
Model
12. Day 1 最终理解
经过 Day 1 的源码阅读,可以先建立这样一个认知:
Superset 是 Flask 应用
↓
使用 Flask-AppBuilder 组织 View、API、菜单和权限
↓
init_views 是 API / 页面 / 菜单注册入口
↓
add_api 注册 REST API
↓
add_view 注册页面和菜单
↓
add_link 注册菜单链接
↓
add_view_no_menu 注册隐藏页面
↓
页面 View 通常返回前端 React 壳子
↓
真正数据由 REST API 提供
掌握这部分之后,再继续看权限体系、当前用户登录态、SQL Lab 执行 SQL,会顺很多。
更多推荐



所有评论(0)