news 2026/9/10 18:17:16

ToolJet 数据库外键(Foreign Key)完整指南:约束、动作与引用完整性实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ToolJet 数据库外键(Foreign Key)完整指南:约束、动作与引用完整性实战

ToolJet 数据库外键(Foreign Key)完整指南:约束、动作与引用完整性实战

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

导读

外键(Foreign Key)是 ToolJet 内置数据库(ToolJet Database)中用于连接两张表、保证数据一致性的核心约束机制。本文基于 ToolJet 官方文档,并结合前端表单实现与后端服务源码,系统讲解在 ToolJet 数据库编辑器中创建外键关系的完整流程、四类更新/删除动作(Restrict、Cascade、Set NULL、Set to Default)的行为差异、引用完整性的底层保障,以及一张表可同时建立多条外键等进阶用法。读完本文,你将能在 ToolJet 数据库 UI 中熟练设计多表关联,并理解外键约束背后的校验规则与源码实现。

什么是外键关系

外键关系(Foreign Key Relation)是指将当前表(Source,源表)中的一个或一组列,与已存在的其他表(Target,目标表)中的一个或一组列进行关联。这一关系在两张表之间建立连接,使源表可以引用目标表的数据。

在 ToolJet 数据库中,当你在源表中创建或编辑表结构时,可以添加一条或多条外键,每条外键都指向另一张已存在表中具备唯一约束的列。创建外键关系时,你还可以为源行选择当目标行被更新删除时执行的动作(Action),相关选项的完整说明见下文"外键动作"一节。

从源码角度看,ToolJet 数据库服务层通过 tooljet-db-table-operations.service.ts 中的createForeignKey方法执行外键创建,内部最终调用 TypeORM 的createForeignKeys生成 PostgreSQL 的FOREIGN KEY ... REFERENCES约束,并在成功后通过NOTIFY pgrst, 'reload schema'通知 PostgREST 重新加载 schema,使新约束立即生效。

Constraints(约束条件)

在 ToolJet 数据库中创建外键关系时,必须满足以下三个约束条件:

  1. 数据类型匹配:目标表中被引用的列,必须与源表中建立外键的列具有相同的数据类型。
  2. 目标列必须具备 Unique 约束:目标表中被引用的列必须显式设置 Unique 约束(也包含主键列,因为主键天然具备唯一性)。
  3. 目标表必须已存在:添加外键关系之前,目标表必须先创建完成,即在源表上建立外键时,无法引用一张尚不存在的表。

在 UI 层,上述约束被编码为类型过滤逻辑:在 TableKeyRelations.jsx 中,目标列下拉列表会根据源列的数据类型动态过滤,仅展示数据类型匹配的候选列;源列为integer时可匹配目标表的integerserial列,源列为bigint时可匹配integerbigint,其余类型则要求完全一致。

Limitations(限制)

除了约束条件,ToolJet 数据库对外键还有以下限制:

  • 不允许自引用:目标表和源表不能是同一张表。
  • serial 类型列不能作为源表的外键列:无法用源表中数据类型为serial的列创建外键。
  • 不能引用复合主键的组成部分:目标表中作为其复合主键(Composite Primary Key)一部分的列,不能被外键引用。

前端源码与上述限制一一对应。在 TableKeyRelations.jsx 中,源列下拉列表将serialbooleantimestamp with time zonejsonb类型的列标记为禁用(isDisabled),无法选为外键源列。而后端在createForeignKey中会调用 checkIfForeignKeyReferencedColumnsAreFromCompositePrimaryKey,遍历目标表的列约束,若目标列属于由多个列组成的复合主键,则抛出ConflictException("Foreign key cannot be created as the referenced column is in the composite primary key."),从服务端再次拦截。

此外,创建外键时源表需至少有一列满足必填字段要求,且数据库中至少要有两张表(目标表加源表),否则 UI 上的+ Add relation按钮会处于禁用状态并给出对应的 tooltip 提示,相关判断见 ForeignKeyRelation.jsx。

Exception(例外)

有一条值得注意的例外规则:

  • 源表中数据类型为integer的列,可以引用目标表中数据类型为serial的列。

这在前端源码中得到印证:在 TableKeyRelations.jsx 的过滤逻辑中,当sourceColumn.dataType === 'integer'时,允许的目标列类型为integerserialserial本质上是 PostgreSQL 基于序列(sequence)生成的整数自增列,因此从值域上看与integer兼容。

Creating Foreign Key(创建外键)

在创建或编辑表(此时该表即为源表)时,你可以添加一条或多条外键,分别引用其他已存在表(目标表)的列。创建外键关系的步骤如下:

  1. 创建或编辑一张表(该表将作为源表)。
  2. Foreign key relation区域点击+ Add relation按钮,打开右侧外键配置抽屉。
  3. Source(源):从下拉菜单中选择要建立外键的列。源表默认为当前正在创建/编辑的表,且已通过数据类型过滤禁用不可用的列。
  4. Target(目标):从下拉菜单中选择目标表,再从该表的下拉菜单中选择被引用的列。目标列列表会随目标表的切换而重新加载(调用viewTable接口拉取目标表列信息),且同样经过数据类型匹配过滤。
  5. Actions(动作):选择目标行被更新或删除时,源表行应执行的动作。
  6. 点击Create按钮创建外键关系。

提示:在目标表的下拉菜单底部有一个进入 ToolJet 数据库页面的入口(源码中通过getPrivateRoute('database')打开数据库主页面),方便你跳转到目标表确认列定义。

创建成功后,源表的外键关系列表会以源列 → 目标表.目标列的形式展示,每条关系旁都有编辑图标,点击可修改目标表、目标列或动作;编辑已有外键关系时,如果更改了目标表或目标列,界面会弹出确认对话框,提示"更新外键关系将先删除当前约束再添加新约束"(对应后端updateForeignKeydropForeignKeycreateForeignKeys的实现,见 tooljet-db-table-operations.service.ts),确认后才会提交变更。删除外键关系同样需要二次确认。

Foreign Key Actions(外键动作)

创建外键关系时,ToolJet 数据库允许你为"目标行被更新或删除时源行执行什么操作"选择动作。On Update 与 On Delete 各有四个选项,前端在 TableKeyRelations.jsx 中维护了onUpdateOptionsonDeleteOptions两个选项列表(RESTRICTCASCADESET NULLSET DEFAULT),且抽屉打开时会默认选中第一项RESTRICT

On Update(目标行更新时)

选项说明
Restrict(默认)若目标表中的某行被其他源行引用,则禁止对该目标行执行更新。
Cascade目标行中的引用值更新后,源表中引用该值的行会同步更新。
Set NULL目标行中的引用值更新后,源表中引用该值的列会被置为 NULL。
Set to Default目标行中的引用值更新后,源表中引用该值的列会被重置为源表外键列的默认值。

On Delete(目标行删除时)

选项说明
Restrict(默认)若目标表中的某行被其他源行引用,则禁止删除该目标行。
Cascade目标行被删除后,源表中引用该值的行会一并被删除。
Set NULL目标行被删除后,源表中引用该值的列会被置为 NULL。
Set to Default目标行被删除后,源表中引用该值的列会被重置为源表外键列的默认值。

这些动作在创建外键时以 JSON 形式提交到后端,字段名为on_deleteon_update(见 ForeignKeyRelation.jsx 中handleCreateForeignKey构造的请求体),最终由createForeignKeys生成对应的 PostgreSQLON UPDATE/ON DELETE子句。实际使用中,最常见的组合是On Delete 选择 Restrict以防止误删被引用的主数据,同时配合On Update 选择 Cascade让主键值变更自动同步到所有关联子表。

Referential Integrity(引用完整性)

外键约束的核心价值在于保证源表与目标表之间的引用完整性(Referential Integrity):它强制源表中的外键列只能取目标表外键列中存在的唯一值之一。ToolJet 数据库在 UI 层面提供了三层保障:

  • 新增行时:在源表新增一行、操作外键列时,该列会以下拉框形式展示目标表中存在的唯一值,确保新增数据与目标表保持一致。
  • 编辑行时:在源表编辑已有行的外键单元格时,下拉框同样只展示目标表中的唯一值,保证更新后的数据始终与目标表一致。
  • 快捷跳转:下拉框底部提供Open referenced table按钮,点击可直接跳转到目标表,便于核对引用数据。

需要注意的是,当插入或更新的值违反了外键约束(例如引用了目标表中不存在的值,或在 Restrict 动作下删除仍被引用的目标行)时,ToolJet 数据库会拒绝该操作并返回约束违规错误,前端会以 toast 形式提示具体失败原因。这一点与后端createForeignKey对 PostgreSQL 错误码(如42710表示外键约束已存在)的处理逻辑对应,见 tooljet-db-table-operations.service.ts。

完整示例:Orders 与 Customers 表

下面通过一个电商场景,演示如何为Orders(订单)与Customers(客户)表建立外键关系。

首先在 ToolJet 数据库中创建以下两张表:

Customers(目标表)

列名数据类型主键非空唯一
customer_idint
namevarchar
emailvarchar

Orders(源表)

列名数据类型主键非空唯一
order_idint
customer_idint
order_datevarchar
total_amountfloat

两张表的结构满足外键约束条件:Orders.customer_idCustomers.customer_id数据类型一致(均为int);目标列Customers.customer_id同时具备主键与唯一约束(主键天然唯一);目标表Customers先于外键创建存在。

现在我们希望在Orders.customer_idCustomers.customer_id之间建立外键关系:

  1. 定义外键关系
    • 编辑Orders表。
    • Foreign key relation区域点击+ Add relation按钮。
    • Source区域选择customer_id列。
    • Target区域选择Customers表和它的customer_id列。
    • 选择目标动作,例如RESTRICT,以防止删除仍有关联订单的客户。
  2. 保存变更:点击Save Changes按钮创建外键关系。

此后,每当向Orders表插入或更新记录时,customer_id的值必须对应Customers表中已存在的customer_id值;同时,由于 On Delete 选择了 Restrict,你无法删除仍被订单引用的客户记录。这样就能确保订单永远关联到一个有效的客户,从而维护数据的完整性与一致性。

外键的进阶用法与底层原理

一条外键关联多个目标列

外键并不局限于单列。ToolJet 数据库支持一个外键关系同时引用目标表的一组列:后端在 prepareForeignKeyDetailsJSON 及 TypeORMTableForeignKey构造中均以数组形式处理referenced_column_names,前端请求体也使用column_names/referenced_column_names数组结构(见 ForeignKeyRelation.jsx),为多列复合外键预留了能力。

一张表多个外键

创建/编辑表时,你可以为不同的源列分别添加多条外键关系,每条关系独立引用不同的目标表。前端foreignKeyDetails状态以数组维护多条关系(handleCreateForeignKey通过[...prevValues]追加新关系),后端createForeignKey同样接收foreign_keys数组并一次性批量创建,见 controller.ts。

外键操作的后端调用链

从一次完整的外键创建操作来看,其调用链为:

  1. 前端 ForeignKeyRelation.jsx 组装column_namesreferenced_table_namereferenced_column_nameson_deleteon_update并调用tooljetDatabaseService.createForeignKey
  2. 服务端 controller.ts 接收请求,将操作委托给tableOperationsService.perform(organizationId, 'create_foreign_key', params)
  3. tooljet-db-table-operations.service.ts 校验目标表存在、复合主键限制后,在事务中执行createForeignKeys,随后NOTIFY pgrst, 'reload schema'刷新 schema 缓存。

其中create_foreign_keyupdate_foreign_keydelete_foreign_key等操作标识集中定义在 constants/index.ts,与前端表单的增、改、删三种操作一一对应。修改已有外键关系的本质是"先删旧约束、再建新约束",因此 UI 才会在变更目标列或目标表时弹出二次确认。

相关文档

  • 主键(Primary Key)约束:外键通常引用主键列,建议先掌握主键的定义方式。
  • 表操作(Table Operations):表的创建、编辑与字段类型定义。
  • 数据库编辑器(Database Editor):ToolJet 数据库的整体界面与数据浏览方式。
  • 查询 ToolJet 数据库:建立外键后,可在查询中利用关联关系进行 Join 查询。

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

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

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

用户验收测试(UAT)全流程指南与实战技巧

1. 用户验收测试的本质与价值 用户验收测试(User Acceptance Testing,简称UAT)是软件交付前的最后一道质量关卡。作为在银行系统做了8年测试的老兵,我见证过太多团队在这个环节翻车——有因为测试用例设计不当导致生产环境崩溃的&…

作者头像 李华
网站建设 2026/9/10 18:13:53

python-dateutil报错排查:从环境错乱到依赖冲突的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 18:11:32

电商数据目录技术:破解PB级数据治理难题

1. 电商行业数据管理的核心挑战与破局思路 在电商行业摸爬滚打多年,我亲眼见证了数据量从GB级到PB级的爆炸式增长。三年前参与某头部电商平台数据中台建设时,我们面对的是分散在47个业务系统的数据孤岛,商品信息在不同系统中存在30%以上的差异…

作者头像 李华
网站建设 2026/9/10 18:11:31

X射线检测揭秘DC-DC电源模块内部结构与失效隐患

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 18:11:27

Hermes Agent运维四层协同更新:Runtime、Orchestration、Skill与Context演进

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 18:10:52

远程评审智能化底座:从音视频通信到AI融合的实践解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华