NocoDB 无代码模式迁移实战:exportSchema / importSchema 脚本实现 Base 结构克隆
2026/9/5 18:42:54 网站建设 项目流程

NocoDB 无代码模式迁移实战:exportSchema / importSchema 脚本实现 Base 结构克隆

【免费下载链接】nocodb🔥 🔥 🔥 A Free & Self-hostable Airtable Alternative项目地址: https://gitcode.com/GitHub_Trending/no/nocodb

本文介绍 NocoDB 仓库中packages/nocodb/tests/export-import/目录下的一套"导出-导入"脚本:它通过 NocoDB REST API(nocodb-sdk)把一个 Base 的完整结构(表、字段、关系型字段、视图及其排序/过滤/表单配置)导出为 JSON 文件,再导入为一个新 Base,并可选地把源 Base 的数据一并复制过去。读完后你能掌握:如何配置config.json、如何执行导出与导入、导出 JSON 的结构是什么、导入脚本按什么依赖顺序重建字段与视图,以及数据回迁阶段分页读取与关系字段修复的具体做法。

一、目录组成与适用场景

这套脚本位于 packages/nocodb/tests/export-import/,包含 4 个文件:

文件作用
ReadMe.md使用说明:config.json 配置项、导出/导入执行命令
config.json运行时配置文件(源/目标 Base 名、服务端地址、认证令牌)
exportSchema.js导出脚本:读取源 Base 结构,写出<srcProject>.json
importSchema.js导入脚本:按 JSON 重建结构,并在源 Base 存在时复制数据

适用场景:在两个 NocoDB 实例(或同一实例内)之间迁移 Base 的"模式(schema)",而不依赖界面手工重建字段关系和视图配置。需要注意其设计前提:结构迁移走 JSON 文件,数据迁移不走 JSON——导入阶段如果源 Base 仍存在于当前实例中,脚本会直接通过 API 从源 Base 分页拉取数据,而不是把行数据写进导出文件。

二、config.json 配置详解

baseURLxc-auth是导入和导出两个脚本共用的配置,完整参数如下(以仓库中实际提交的 config.json 为准):

{ "srcProject": "sample", "dstProject": "sample-copy", "excludeDt": true, "baseURL": "http://localhost:8080", "xc-auth": "Copy Auth Token" }
  • srcProject:源 Base 的名称。导出时,脚本会用它定位 Base 并生成srcProject.json(名称中的空格会被替换为下划线);导入时,它表示"要导入的 JSON 文件名(不含 .json 后缀)"。
  • dstProject:导入时新 Base 的名称,仅导入阶段使用。
  • excludeDt:仅 exportSchema.js 使用的可选项。为true时,导出 JSON 中的字段会省略dt(底层数据类型)字段,只保留uidt(UI 类型)等字段,让导入端根据uidt自行决定底层类型;为false或省略时,dt也会一并导出。脚本源码中有一个明确的fixme注释:当字段默认值(cdf)被配置为0/null/false时,removeEmpty过滤逻辑会把它们一并剔除,这是已知边界。
  • baseURL:NocoDB 服务端地址,如http://localhost:8080
  • xc-auth:API 认证令牌(个人访问令牌),占位值"Copy Auth Token"需替换为真实值。

两个脚本都会把上述配置组装为 SDK 的连接参数,其中xc-auth被放进请求头:

// exportSchema.js 中的配置装载(importSchema.js 同理) let ncConfig = { baseName: inputConfig.srcProject, baseURL: inputConfig.baseURL, headers: { 'xc-auth': `${inputConfig["xc-auth"]}` } };

运行前提:NocoDB 服务端已启动(默认监听 8080)、目标导入的 Base 名可重复创建(见下文导入流程,脚本会先删后建),且nocodb-sdkjsonfile依赖可用——nocodb-sdk在 packages/nocodb/package.json 中声明为workspace:^,指向本仓库的 packages/nocodb-sdk。

三、执行导出:node exportSchema.js

cd packages/nocodb/tests/export-import node exportSchema.js

执行后,脚本在当前目录生成srcProject.json(如sample.json)。导出流程可拆为四步,全部基于 exportSchema.js 的源码:

3.1 定位 Base 并建立 ID-名称映射

exportSchema()先调用api.base.list(),按标题找到源 Base,然后执行generateMapTbl(p.id)。这一步遍历所有表,把表 ID、字段 ID、视图 ID、视图字段 ID 四类 ID 分别映射到名称,存入全局ncMap。之所以要建这张映射表,是因为 NocoDB 内部大量引用的是随机生成的 ID(如fk_relation_column_id),而跨实例迁移时 ID 必然变化,必须改用名称做锚点,这正是导出/导入脚本"以 title 为键"的根源。

3.2 收集每个视图的列、排序、过滤详情

storeViewDetails(tableId)对每张表的每个视图按类型分别取数:

  • Form(type=1)api.dbView.formRead(v.id)取表单列(含labelrequireddescription等);
  • Gallery(type=2)api.dbView.galleryRead(v.id),额外保留封面图字段fk_cover_image_col_id
  • Grid(type=3)api.dbView.gridColumnsList(v.id),保留列宽width
  • 所有非 Form 视图还会读取api.dbTableSort.list(v.id)(排序:fk_column_iddirectionorder)与api.dbTableFilter.read(v.id)(过滤:fk_column_idlogical_opcomparison_opvalueorder)。

视图自身的property对 Form 视图会导出headingsubheadingsuccess_msgredirect_after_secsemailsubmit_another_formshow_blank_form等表单属性(见 addViewDetails)。

3.3 字段级数据的裁剪:addColumnSpecificData

每个字段默认只保留idtitlecolumn_nameuidtpkpvrqddtxpsystemai(以及非 excludeDt 时的dt),再经removeEmpty剔除空值(null/0/false)。对四种"重字段"附加colOptions

字段类型(UITypes附带的 colOptions
Formulaformulaformula_raw
LinkToAnotherRecordfk_model_idfk_related_model_idfk_child_column_idfk_parent_column_idtype(hm/bt/mm)
Lookupfk_model_idfk_relation_column_idfk_lookup_column_id
Rollupfk_model_idfk_relation_column_idfk_rollup_column_idrollup_function

导出时还会做三类过滤,把系统自动生成的镜像/占位列剔除:排除标题含_nc_m2m_的多对多中间列、排除含前缀(hm 列)的列、排除system === 1且类型为 LinkToAnotherRecord 的系统链接列(见 exportSchema.js 主循环)。

3.4 写出 JSON

最终结构是一个数组,每个元素形如:

[ { "id": "表ID", "title": "表名", "table_name": "物理表名", "columns": [ { "id": "...", "title": "字段名", "uidt": 2, "pk": 1, "colOptions": { ... } } ], "views": [ { "id": "...", "title": "视图名", "type": 3, "columns": [...], "sort": [...], "filter": [...] } ] } ]

写入时把 Base 名中的空格替换成下划线:`${ncConfig.baseName.replace(/ /g, '_')}.json`,以 2 空格缩进保存。

四、执行导入:node importSchema.js

cd packages/nocodb/tests/export-import node importSchema.js

导入阶段的srcProject指向要导入的 JSON 文件名(不含.json),dstProject是新 Base 名称。importSchema.js 的importSchema()主函数定义了严格的执行顺序:

base.list -> 删除同名 dstProject(若存在)-> base.create(dstProject) -> createBaseTables -> createLinks -> createLookup -> createRollup -> createFormula -> configureGrid -> configureGallery -> configureForm -> (若源 Base 仍存在)restoreBaseData -> restoreLinks

4.1 建表:先建普通字段,关系型字段延后

createBaseTables()对 JSON 中每张表,把字段分成两拨:普通字段随api.dbTable.create()一次性建出;LinkToAnotherRecord、Lookup、Rollup、Formula 四类字段被收集到全局数组link/lookup/rollup/formula中留待后续阶段。原因很直接:链接字段依赖双方表存在,Lookup/Rollup 依赖链接字段,Formula 依赖被引用的普通字段——必须按依赖拓扑分阶段创建。

建表时同时维护一张核心映射表ncTables,它用v1 表 ID、v2 表 ID、表标题三个键指向同一个新表对象,后续所有"旧 ID → 新 ID"的换算都靠它(配合字段 title 匹配)完成。

4.2 链接字段:对称列识别与 mm 去重

createLinks()处理hm(one-to-many 宿主侧)与mm(many-to-many)两类链接(bt侧由 NocoDB 服务端在创建 hm/mm 时自动生成对称列):

  • mm 去重isLinkCreated()(child, parent)列 ID 对判重,避免一张多对多关系被两侧表各创建一次;
  • 对称列改名:创建链接后会重新api.dbTable.read()目标表,按fk_parent_column_id/fk_child_column_id的互换关系找出服务端自动生成的对称列,再把它重命名为 JSON 中记录的原始标题(v1SymmetricColumn.title)——这一步保证导入后的界面标题与源 Base 一致;
  • rootLinks记录:每个成功创建的链接连同源表一起入队,供最后的数据关系修复(restoreLinks)使用。

4.3 Lookup / Rollup / Formula 的 ID 重映射

Lookup 与 Rollup 创建前必须把 JSON 里的 v1 ID 翻译成 v2 ID,换算函数是get_v2Id():先在 JSON 中按 v1 字段 ID 找到字段 title,再在新表中按 title 找到对应列的 v2 ID。Rollup 额外带上rollup_function(聚合函数)。Formula 则只回写formula_raw(公式文本),由服务端重新解析生成 SQL。

脚本文件头部注释明确列出了两个已知边界(tbd):公式依赖列表嵌套 Lookup/Rollup尚未完整处理,即公式若引用了 Lookup/Rollup 列或跨表嵌套引用时可能失败,使用时需人工验证。

4.4 视图重建:Grid / Gallery / Form

  • configureGrid():每张表新建表时 NocoDB 自带一个默认 Grid 视图,脚本把第一个导出 Grid 直接重命名为默认视图(api.dbView.update),其余用api.dbView.gridCreate创建。随后逐列更新可见性(show)、顺序(order)、列宽(gridColumnUpdate),再逐条创建排序(dbTableSort.create,携带direction)与过滤(dbTableFilter.create)。从源码看,过滤条件列名反查时误用了sort[fCnt].fk_column_id(importSchema.js 该处),属于脚本遗留缺陷:过滤列与排序列不一致时可能定位错列,实际使用中建议核对过滤结果。
  • configureGallery():逐个api.dbView.galleryCreate创建同名视图(当前实现仅还原标题,未迁移封面图配置)。
  • configureForm()api.dbView.formCreate时携带导出的property(heading、subheading、success_msg、redirect_after_secs、email、submit_another_form、show_blank_form 等),然后逐列更新表单列的show/order/label/description/required,列 ID 通过nc_getViewColumnId()fk_column_id在新视图的列集合中反查获得。

4.5 数据回迁:从源 Base 实时分页复制

这是 ReadMe.md 中"数据导入不经过导出 JSON"的具体实现。当api.base.list()里能找到源 Base(srcProject)时,脚本执行restoreBaseData()+restoreLinks()

  1. restoreBaseData():以 25 条为一页(limit=25, offset递增)循环api.dbTableRow.list('nc', srcProject, 表名, ...),直到pageInfo.isLastPage;每条记录再用主键(pk字段 title)read出完整数据,然后删除 Link/Lookup/Rollup 字段的值(这些字段在目标端会自动计算或由后续步骤恢复),最后api.dbTableRow.create('nc', baseName, 表名, record)写入新 Base;
  2. restoreLinks():遍历rootLinks,同样分页读取源表记录,取出链接字段值,用api.dbTableRow.nestedAdd(...)在新 Base 中逐条把链接关系挂回(nestedAdd是 nocodb-sdk Api 提供的嵌套关联操作,用于在记录上追加关联行 ID)。

由于数据是通过 REST API 逐行读写的,这个流程是"结构性迁移 + API 数据搬运"的组合方案:源 Base 必须可达、令牌必须对源/目标都有读写权限;若源 Base 不存在,则只做纯结构迁移。

五、使用限制与排错清单

结合 ReadMe.md 与两份脚本源码,可以确认的边界条件:

  • 导出文件名约定:导出产物固定为srcProject.json(空格转下划线);导入时srcProject配置值必须与该文件名(去.json)一致,jsonfile.readFileSync是按此拼接的;
  • dstProject同名的旧 Base 会被删除importSchema()开头if (p) await api.base.delete(p.id),重复执行导入前请确认目标 Base 中无需要保留的数据;
  • mm 链接列只建一侧:导入靠isLinkCreated判重,一张多对多关系在目标端只由一侧触发创建,另一侧对称列由服务端自动生成后被改名对齐;
  • 公式与嵌套引用是已知缺口:脚本头部tbd注释声明未处理 formula 依赖列表与嵌套 lookup/rollup;
  • Gallery 视图迁移不完整:导出保留了封面图字段信息,但configureGallery()目前只创建同名视图;
  • 认证失败xc-auth为占位符或过期会导致所有 SDK 调用报错,脚本仅console.log错误,无重试;
  • 数据回迁的前提:源 Base 存在于同一实例且令牌可读;行数据按主键逐条 read + 逐条 create,大批量数据下速度受 API 往返限制(每页 25 条)。

六、小结

这套脚本给出了 NocoDB 基于公开 REST API 做 Base 级迁移的完整参考实现:导出端以"名称映射表 + 分类型字段裁剪"生成可移植的 schema JSON;导入端以"普通字段 → 链接 → Lookup → Rollup → Formula → 视图 → 数据 → 关系数据"的依赖顺序重建结构,并用 title 作为跨实例 ID 重映射的锚点。对于需要程序化克隆、备份或跨环境部署 NocoDB Base 结构的场景,exportSchema.js 与 importSchema.js 中api.dbTable.*api.dbView.*api.dbTableRow.nestedAdd等 SDK 调用链本身就是最可靠的 API 用法示例;使用时应结合第五节的限制清单,对公式依赖、Gallery 配置与批量数据耗时做针对性验证。

【免费下载链接】nocodb🔥 🔥 🔥 A Free & Self-hostable Airtable Alternative项目地址: https://gitcode.com/GitHub_Trending/no/nocodb

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询