本文所有代码和配置是基于superset Version 0.36.0
配置本地环境
首先从 Superset Github clone 代码到本地,并切换到0.36.0 分支
# clone repo
git clone https://github.com/apache/incubator-superset.git
# checkout release 0.36.0
cd incubator-superset
git checkout tags/0.36.0
提前需要装好python 3.7,建议使用pyenv,不要用mac的system python
brew install pyenv
pyenv install 3.7.0
// 在当前superset目录下,设置python版本为3.7.0,
pyenv local 3.7.0
之后在根目录下 CONTIBUTING.md, 找到 Setup Local Environment for Development
根据文档,分别把后台和前端run起来
- 后台flask server
# 安装各种依赖
pip install -r requirements.txt
pip install -r requirements-dev.txt
pip install -e .
# 创建admin用户,记得记住用户名密码,之后要登陆
superset fab create-admin
# 脚本初始化和迁移数据库
superset db upgrade
# 脚本初始化用户和组的权限
superset init
# 脚本加载默认的图表和面板
superset load_examples
# run flask server
FLASK_ENV=development superset run -p 8088 --with-threads --reload --debugger
成功以后,后台会在 http://localhost:8088 运行
2. 前端
cd superset-frontend
# 使用package.lock 安装依赖
npm ci
# run dev server
npm run dev-server
打开 http://localhost:9000,界面如下
如果能看到页面,说明前后台的环境都配置好了。就可以开始添加自定义图表了。
添加自定义图表
首先打开一个常规的创建/编辑 图表页面,左侧为 控制面板,右侧为图表。不同的图表的配置面板会有差异。
所以要添加一个自定义图表,主要有三步
- 【前端】添加自定义图表的面板 Data Tab
- 【前端】添加自定义图表以及面板的 Customize Tab
- 【后台】添加后台接口返回正确的数据给前端渲染。
1. 添加自定义图表的控制面板 Data Tab
控制面板主要分为两个部分
- Data 控制输出给图表的数据,做一些聚合,排序,计算的操作
- Customize 自定义,针对图表进行主题颜色替换,以及相关的显示配置,如表格是否分页,以及每页显示几条,以及是否添加搜索框
所以如果添加一个自定义图表,前端需要添加一个自定义的图表,以及图表对应的控制面板。
例如,我需要添加一个echarts的雷达图,这个在superset中没有类似的实现。
经过分析和对比,发现现在superset的pie chart的数据结构跟雷达图类似,那么可以把复用Pie Chart的控制面板的配置文件。
在 superset-frontend/src/explore/components/controls/index.js 这个文件中有所有的控制面板的组件,例如 MetricsControl 对应的就是页面中的Metric下拉框。如果你需要添加自定义的控制组件,可以在这里添加。
找到Pie Chart的控制面板配置文件, superset-frontend/src/explore/controlPanels/Pie.js
import { t } from '@superset-ui/translation';
export default {
controlPanelSections: [
// label对应的是每个section的标题
// expand对应的是改section是否可以被展开
// controlSetRows 定义的是每一行有几个组件,metric,adhoc_filters对应的是组件的key
// 可以在superset-frontend/src/explore/controls.jsx
// 中通过这些key找到对应的配置
{
label: t('Query'),
expanded: true,
controlSetRows: [
['metric'],
['adhoc_filters'],
['groupby'],
['row_limit'],
],
},
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
['pie_label_type', 'number_format'],
['donut', 'show_legend'],
['show_labels', 'labels_outside'],
['color_scheme', 'label_colors'],
],
},
],
controlOverrides: {
row_limit: {
default: 25,
},
number_format: {
description:
t('D3 format syntax: https://github.com/d3/d3-format') +
' ' +
t('Only applies when the "Label Type" is not set to a percentage.'),
},
},
};
在controlPanelSections中,controlSetRows对应的是配置面板中的layout以及对应的组件,例如,metric在superset-frontend/src/explore/controls.jsx的值为
const metrics = {
type: 'MetricsControl',
multi: true,
label: t('Metrics'),
validators: [v.nonEmpty],
default: c => {
const metric = mainMetric(c.savedMetrics);
return metric ? [metric] : null;
},
mapStateToProps: state => {
const datasource = state.datasource;
return {
columns: datasource ? datasource.columns : [],
savedMetrics: datasource ? datasource.metrics : [],
datasourceType: datasource && datasource.type,
};
},
description: t('One or many metrics to display'),
};
// label 为该组件显示在控制面板的名称
// type 为对应的UI组件
const metric = {
...metrics,
multi: false,
label: t('Metric'),
description: t('Metric'),
default: props => mainMetric(props.savedMetrics),
};
这里metric对应的type是 MetricsControl
可以在superset-frontend/src/explore/components/controls/index.js中找到MetricsControl对应的UI组件为 superset-frontend/src/explore/components/controls/MetricsControl.jsx
这样控制面板就通过superset-frontend/src/explore/controlPanels文件夹下面的js文件,对不同类型的图表进行配置。
那么现在来添加自定义雷达图的控制面板
- 在这个目录 superset-frontend/src/explore/controlPanels 下创建一个新的文件 CustomTable.js, 为了防止以后superset也会支持雷达图,加了一个前缀custom防止重名。此处我直接复制Pie.js,重命名为CustomTable.js
- 在superset-frontend/src/setup/setupPlugins.ts 注册一下新的控制面板配置,代码如下
// ... 此处省略
import { getChartControlPanelRegistry } from '@superset-ui/chart';
import MainPreset from '../visualizations/presets/MainPreset';
import setupPluginsExtra from './setupPluginsExtra';
import Area from '../explore/controlPanels/Area';
// ... 此处省略
// 引入自定义的控制面板配置
import CustomTable from '../explore/controlPanels/CustomTable';
export default function setupPlugins() {
new MainPreset().register();
// TODO: Remove these shims once the control panel configs are moved into the plugin package.
getChartControlPanelRegistry()
.registerValue('area', Area)
// ... 此处省略
// 注册控制面板,custom_table就是我将要引入的图表的key
// 那么如果图表的key是custom_table的时候,
// 页面会使用CustomRader这个配置来显示控制面板
.registerValue('custom_radar', CustomRader);
setupPluginsExtra();
}
那么控制面板就添加好了,接下来添加雷达图
2. 添加自定义图表
- 在superset-frontend/src/visualizations底下新建目录 CustomRadar
也可以复制Pie Chart的目录以及文件格式,Pie Chart的实现在另一个repo
打开Pie Chart的目录
- images对应的是Pie Chart的图标,包括一大一小
- controPanel.ts 对应的是控制面板的Customize Tab,就是自定义配置,因为每个图表的自定义配置差别比较大,所以跟图表放在了一起。
- index.js 对应的是图表具体的实现
在 superset-frontend/src/visualizations/presets/MainPreset.js 中注册新的图表
import { Preset } from '@superset-ui/core';
// ...
import {
AreaChartPlugin,
// ...
} from '@superset-ui/legacy-preset-chart-nvd3';
// 引入自定义图表
import CustomRadar from '自定义组件的目录/custom-radar';
export default class MainPreset extends Preset {
constructor() {
super({
name: 'Legacy charts',
presets: [new DeckGLChartPreset()],
plugins: [
new AreaChartPlugin().configure({ key: 'area' }),
// ...
// 注册自定义图表
new CustomRadar().configure({ key: 'custom-radar' }),
],
});
}
}
注册完以后,可以参照其他的plugin的实现来实现自定义的图表。
3. 添加对应接口
添加完自定义图表和控制面板以后,还需要配置相应的接口来拿取数据,打开python文件
superset/viz.pygithub.com里面包含了所有图表获取数据的接口,所有接口的实现都需要继承BaseViz,BaseViz的具体实现可以查看链接,例如Pie图表,定义了viz_type, verbose_name, credits, is_timeseries, 这些参数是每个图表都需要定义的。之后实现get_data这个方法就可以了。df 参数为pandas的dataFrame数据。可以对这个数据进操作之后返回。
其实BaseViz里面实现的get_json调用了get_payload,之后才调用了get_data。所以继承了BaseViz以后只需要重写get data方法就可以了。
// ...
class DistributionPieViz(NVD3Viz):
"""Annoy visualization snobs with this controversial pie chart"""
viz_type = "pie"
verbose_name = _("Distribution - NVD3 - Pie Chart")
is_timeseries = False
def get_data(self, df: pd.DataFrame) -> VizData:
if df.empty:
return None
metric = self.metric_labels[0]
df = df.pivot_table(index=self.groupby, values=[metric])
df.sort_values(by=metric, ascending=False, inplace=True)
df = df.reset_index()
df.columns = ["x", "y"]
return df.to_dict(orient="records")
// ...
viz_types = {
o.viz_type: o
for o in globals().values()
if (
inspect.isclass(o)
and issubclass(o, BaseViz)
and o.viz_type not in config["VIZ_TYPE_BLACKLIST"]
)
}
之后重启后台和前端就能使用自定义的图表了。