news 2026/9/19 5:20:24

NocoBase RunJS 上下文实战:用 ctx.dataSource 访问数据源、数据表与字段元数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NocoBase RunJS 上下文实战:用 ctx.dataSource 访问数据源、数据表与字段元数据

NocoBase RunJS 上下文实战:用 ctx.dataSource 访问数据源、数据表与字段元数据

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

在 NocoBase 的 RunJS 执行环境中,ctx.dataSource是连接“无代码界面”与“数据模型元数据”的关键入口:它是当前执行上下文绑定的数据源实例(DataSource),让你在已知当前数据源时直接获取数据表(Collection)、字段定义以及关联字段(Association),并管理当前数据源下的数据表配置。读完本文,你将掌握ctx.dataSource的全部常用属性与方法、collectionName.fieldPath路径语法的工作原理、它与ctx.dataSourceManager(跨数据源入口)的分工边界,并能结合 flow-engine 中的 DataSource 实现 理解其底层机制,从而在 RunJS 脚本中完成数据表枚举、动态校验、按路径取字段等实战场景。

一、什么是 ctx.dataSource

ctx.dataSource的类型是DataSource,表示当前 RunJS 执行上下文绑定的数据源实例。通常对应当前页/区块选中的数据源(如主库main)。

属性/成员类型说明
keystring数据源 key,如'main'
namestring同 key
displayNamestring显示名称(支持 i18n)
flowEngineFlowEngine当前 FlowEngine 实例

从源码实现看,这些属性都是DataSource类上的 getter:keyname直接返回options.keydisplayName会先通过flowEngine.translate(this.options.displayName, { ns: 'lm-collections' })做多语言翻译,翻译不到时才回退为 key。相关实现见 DataSource 类定义:

get displayName() { return this.flowEngine.translate(this.options.displayName, { ns: 'lm-collections' }) || this.key; } get key() { return this.options.key; } get name() { return this.options.key; }

这说明displayName是可以被界面多语言覆盖的展示名,而key才是程序访问数据源时应当使用的稳定标识。

适用场景

场景说明
单数据源操作在已知当前数据源时,获取数据表、字段元数据
数据表管理获取/添加/更新/删除当前数据源下的数据表
按路径获取字段使用collectionName.fieldPath格式获取字段定义(支持关联路径)

注意:ctx.dataSource表示当前上下文的单一数据源;若要枚举或访问其他数据源,请使用 ctx.dataSourceManager。

二、类型定义与 API 全貌

DataSource在 RunJS 上下文中暴露的核心 API 如下:

dataSource: DataSource; class DataSource { constructor(options?: Record<string, any>); // 只读属性 get flowEngine(): FlowEngine; // 当前 FlowEngine 实例 get displayName(): string; // 显示名称(支持 i18n) get key(): string; // 数据源 key,如 'main' get name(): string; // 同 key // 数据表读取 getCollections(): Collection[]; // 获取所有数据表 getCollection(name: string): Collection | undefined; // 按名称获取数据表 getAssociation(associationName: string): CollectionField | undefined; // 获取关联字段(如 users.roles) // 数据表管理 addCollection(collection: Collection | CollectionOptions): void; updateCollection(newOptions: CollectionOptions): void; upsertCollection(options: CollectionOptions): Collection | undefined; upsertCollections(collections: CollectionOptions[], options?: { clearFields?: boolean }): void; removeCollection(name: string): void; clearCollections(): void; // 字段元数据 getCollectionField(fieldPath: string): CollectionField | undefined; }

与类型声明一一对应的是 flow-engine 中 DataSource 的源码:这些方法大多是对内部CollectionManager的委托,例如getCollections()返回this.collectionManager.getCollections()getCollection(name)返回this.collectionManager.getCollection(name)。这带来两个实践结论:

  1. 所有数据表状态都保存在CollectionManagerCollectionManager使用observable.shallow<Map<string, Collection>>存放数据表(见 CollectionManager 构造函数),对数据表进行增删改时会自动重置继承链缓存(resetCaches),因此在 RunJS 中调用upsertCollections等写方法后,后续读取的元数据立即可见且保持响应式。
  2. 旧拼写getAssocation已标记废弃:源码中保留了getAssocation(associationName)作为兼容别名并转发到getAssociation(见 getAssociation 实现)。新脚本请统一使用getAssociation

常用方法速查

方法说明
getCollections()获取当前数据源下所有数据表(已排序、过滤隐藏)
getCollection(name)按名称获取数据表;name可为collectionName.fieldName获取关联目标数据表
getAssociation(associationName)collectionName.fieldName获取关联字段定义
getCollectionField(fieldPath)collectionName.fieldPath获取字段定义,支持关联路径如users.profile.avatar

三、getCollectionField:路径解析的工作原理

getCollectionField(fieldPath)的入参格式为collectionName.fieldPath:第一段为数据表名,后续为字段路径(支持关联,如user.name)。其解析逻辑在源码中非常清晰(getCollectionField 实现):

getCollectionField(fieldPath: string) { const [collectionName, ...otherKeys] = fieldPath.split('.'); const fieldName = otherKeys.join('.'); const collection = this.getCollection(collectionName); if (!collection) { return; } const field = collection.getFieldByPath(fieldName); if (!field) { return; } return field; }

可以看出三步解析过程:

  1. .拆分,第一段解析出collectionName,其余拼回为字段路径;
  2. 通过this.getCollection(collectionName)拿到数据表——如果数据表不存在则直接返回undefined
  3. 调用数据表的getFieldByPath(fieldName)沿字段路径逐段查找,中途任一段不存在都会返回undefined

因此调用方必须对返回结果做空值判断。与它相对的是跨数据源版本 DataSourceManager.getCollectionField:先拆出第一段作为数据源 key 取出对应数据源,再把剩余路径交给该数据源的getCollectionField处理,即main.users.profile.avatar等价于“在 main 数据源内执行getCollectionField('users.profile.avatar')”。

四、实战示例

以下示例继承自官方文档,均假设 RunJS 上下文已绑定当前数据源。

4.1 获取数据表及字段

// 获取所有数据表 const collections = ctx.dataSource.getCollections(); // 按名称获取数据表 const users = ctx.dataSource.getCollection('users'); const primaryKey = users?.filterTargetKey ?? 'id'; // 按「数据表.字段路径」获取字段定义(支持关联) const field = ctx.dataSource.getCollectionField('users.profile.avatar'); const userNameField = ctx.dataSource.getCollectionField('orders.createdBy.name');

要点:filterTargetKey是数据表的主键字段名(filterTargetKey在 CollectionOptions 类型 中定义为string | Array<string>),未配置时应回退到默认的'id'

4.2 获取关联字段

// 按 collectionName.fieldName 获取关联字段定义 const rolesField = ctx.dataSource.getAssociation('users.roles'); if (rolesField?.isAssociationField()) { const targetCol = rolesField.targetCollection; // 按目标数据表结构处理 }

getAssociation('users.roles')返回的是roles这个关联字段的定义本身,targetCollection则指向关联指向的目标数据表,便于你按目标表结构继续做元数据操作。

4.3 遍历数据表做动态处理

const collections = ctx.dataSource.getCollections(); for (const col of collections) { const fields = col.getFields(); const requiredFields = fields.filter((f) => f.options?.required); // ... }

从 ICollection 接口定义 可以看到,数据表对象提供getFields()getField(name)getFieldByField(field)setFieldremoveFieldupdateOptions等能力,且每个字段都带有options: FieldOptionsFieldOptions中除nametypeinterface外,还包含primaryKeyuniqueallowNullautoIncrementdefaultValue等数据库语义字段(见 FieldOptions 类型),这些正是遍历数据表做“必填项统计”“主键推断”等动态处理时的判断依据。

4.4 根据字段元数据做校验或动态 UI

const field = ctx.dataSource.getCollectionField('users.status'); if (field) { const options = field.enum ?? []; const operators = field.getFilterOperators(); // 根据 interface、enum、validation 等做 UI 或校验 }

典型应用:读取field.interface决定渲染哪种输入控件,读取field.enum生成下拉选项,调用field.getFilterOperators()获取该字段支持的筛选运算符集合,从而在 RunJS 驱动的表单/筛选器中自动生成校验规则。

五、ctx.dataSource 与 ctx.dataSourceManager 的分工

两者同属 RunJS 上下文,但职责边界不同,选择错误的入口是常见的脚本错误来源:

需求推荐用法
当前上下文绑定的单一数据源ctx.dataSource
所有数据源入口ctx.dataSourceManager
当前数据源内获取数据表ctx.dataSource.getCollection(name)
跨数据源获取数据表ctx.dataSourceManager.getCollection(dataSourceKey, collectionName)
当前数据源内获取字段ctx.dataSource.getCollectionField('users.profile.avatar')
跨数据源获取字段ctx.dataSourceManager.getCollectionField('main.users.profile.avatar')

从源码结构看,DataSourceManager内部维护dataSources: Map<string, DataSource>这一注册表(见 DataSourceManager 类),并提供addDataSource/upsertDataSource/removeDataSource/clearDataSources等管理方法。两点值得注意:

  • addDataSource在 key 已存在时会直接抛出DataSource with name ${ds.key} already exists异常,需要“覆盖或新增”语义时应改用upsertDataSource
  • getDataSource(key)在数据源不存在时返回undefined,使用前建议做空值判断;
  • 数据源还支持通过registerLoader/ensureLoaded异步加载元数据并维护statusloading/loaded/loading-failed等)状态,DataSource.statusErrorMessagegetter 也对应暴露了这些状态(见 status/errorMessage getter)。这意味着在数据源尚未加载完成时读取元数据可能拿到空结果,编写健壮脚本时应留意数据源状态。

六、注意事项与健壮性建议

  • 路径格式getCollectionField(fieldPath)的路径格式为collectionName.fieldPath,第一段为数据表名,后续为字段路径(支持关联,如user.name)。
  • 关联目标数据表getCollection(name)支持collectionName.fieldName形式,返回关联字段的目标数据表。
  • 可能为 undefinedctx.dataSource在 RunJS 上下文中通常由当前区块/页面的数据源决定;若上下文无绑定数据源,可能为undefined,使用前建议做空值判断。同理,getCollectiongetCollectionFieldgetAssociation在找不到对象时都会返回undefined,链式访问时应使用?.与回退值。

七、延伸阅读

  • ctx.dataSourceManager:数据源管理器,管理所有数据源
  • ctx.collection:当前上下文关联的数据表
  • ctx.collectionField:当前字段的数据表字段定义
  • DataSource 源码实现:RunJS 所用DataSource/DataSourceManager的完整实现
  • 数据源核心包:服务端DataSource抽象类,展示ctx.dataSource在请求中间件中的注入方式(middleware 注入逻辑)

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 5:21:51

GAMS在碳封存优化中的应用:从数学建模到工程实践

1. 项目概述&#xff1a;当优化建模遇上地质封存做能源优化这行的朋友&#xff0c;最近应该都注意到了GAMS和TotalEnergies在碳封存领域合作的消息。乍一看这可能只是条普通的企业新闻&#xff0c;但往深了想&#xff0c;这其实是数学优化软件与能源巨头在减碳路径上的一次关键…

作者头像 李华