NocoBase 一对多(O2M)关系字段:配置、ON DELETE 规则与底层实现

发布时间:2026/9/14 18:52:25
NocoBase 一对多(O2M)关系字段:配置、ON DELETE 规则与底层实现 NocoBase 一对多O2M关系字段配置、ON DELETE 规则与底层实现【免费下载链接】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 官方文档中的「一对多」关系字段说明完整梳理 One to manyO2M源码中对应 HasMany关系字段的适用场景、全部配置参数与 ON DELETE 级联删除规则并结合 NocoBase 仓库中HasManyField、ReferencesMap与引用完整性检查的源码实现讲清关系字段从界面配置到数据库关联建立的完整链路。读完本文你可以独立完成 O2M 关系建模、正确选择外键与删除策略并理解 NocoBase 在应用层执行 ON DELETE 的底层机制。什么是一一对多关系班级与学生的例子一对多关系描述的是一个实体可以关联多个子实体但一个子实体只能属于一个父实体。官方文档给出的典型例子是班级和学生的关系——一个班级可以有多个学生但一个学生只能属于一个班级两者之间就是一对多关系。从上图的 ER 关系可以看出 O2M 建模的两个要点关系字段配置在“一”的一方如 Classes 表中的 Students 字段类型为 One to many“多”的一方Students 表保存外键字段如 Class ID来指向父记录。NocoBase 支持的关系字段类型还包括一对一、多对一和多对多可分别参见 一对一、多对一 和 多对多 文档总览见 关系字段。一对多字段配置界面与操作步骤在数据建模中进入目标数据表如 Classes的「添加字段」字段接口类型选择One to many后即可进入一对多关系配置。字段界面说明One to many 用于创建一对多关系例如一个 country 拥有多个 city而一个 city 只属于一个 country当它以字段形式存在时它是一个展示关联集合中记录的子表区块。创建后会在关联的数据表中自动生成一个 Many-to-one多对一字段。以「班级-学生」为例配置界面中的各项取值配置项示例取值说明Field interfaceOne to many字段接口类型Field display nameStudents字段显示名称Field namestudents字段名支持字母、数字、下划线必须以字母开头Source collectionClasses当前字段所在表源表Target collectionStudents要关联的表目标表Source keyID外键约束引用的源表字段字段值必须唯一Foreign keyClass ID目标表中用于建立关联的外键字段可随机生成后修改Target keyID目标表字段字段值必须唯一ON DELETESET NULL删除源表记录时对子表外键引用的操作规则Create inverse field in target collection勾选是否在目标集合中创建反向多对一字段参数说明Source collection源表源表也就是当前字段所在表。在班级-学生示例中源表是 Classes因为关系字段配置在 Classes 上。Target collection目标表目标表即与源表关联的另一张表示例中为 Students。Source key源键外键约束引用的字段必须具备唯一性。通常直接选源表主键示例中的 ID。从源码实现看HasManyField未显式指定sourceKey时会默认取源模型的主键属性见checkAssociationKeys()中的处理逻辑has-many-field.ts。Foreign key外键目标表的字段用于建立两个表之间的关联。界面提示该字段名可随机生成并修改支持字母、数字和下划线且必须以字母开头。这里有两点源码层面的事实值得注意默认命名规则如果未手动指定外键名NocoBase 会按「源表单数名 源键」驼峰拼接生成默认外键名例如 Classes 表的students字段sourceKey 为id默认生成classesId实现见 HasManyField.foreignKeyget foreignKey() { if (this.options.foreignKey) { return this.options.foreignKey; } const { model } this.context.collection; return Utils.camelize([model.options.name.singular, this.sourceKey || model.primaryKeyAttribute].join(_)); }图中显示的Class ID属于早期版本的命名示例当前仓库代码生成的是驼峰命名。类型校验绑定关联时checkAssociationKeys()会比对目标表外键字段与源表源键字段的类型不一致时直接抛出错误如Foreign key xxx type yyy does not match source key ...见 has-many-field.ts。Target key目标键目标表的字段用于关系区块中每行记录的查看与定位一般为具备唯一性的字段通常为主键。ON DELETE删除规则ON DELETE 定义删除源表父表记录时对目标表子表中外键引用的处理规则是建立外键约束时的一个重要选项。官方文档列出了四种常见取值CASCADE删除父表记录时自动删除子表中与之关联的所有记录SET NULL删除父表记录时将子表中与之关联的外键值设为 NULLRESTRICT默认选项当试图删除父表记录时如果存在与之关联的子表记录则拒绝删除NO ACTION与 RESTRICT 类似如果存在与之关联的子表记录则拒绝删除父表记录。HasManyFieldOptions接口对onDelete的 JSDoc 注释给出了与关系类型绑定的默认值说明多对一1:m场景默认SET NULL多对多场景默认CASCADE见 HasManyFieldOptions。同时该接口还暴露了onUpdate更新时级联默认CASCADE、constraints/foreignKeyConstraint是否启用数据库层外键约束等选项可供插件开发者在代码中创建关系字段时参考。底层实现O2M 关系是如何建立的bind()注册 Sequelize 关联并维护元数据NocoBase 的字段模型定义在 packages/core/database/src/fields/has-many-field.ts 中HasManyField继承自RelationField其dataType为HasMany。核心流程在bind()方法中has-many-field.ts类型校验先调用checkAssociationKeys()校验源键与外键字段类型一致注册关联调用collection.model.hasMany(Target, { constraints: false, as: this.name, foreignKey: this.foreignKey, ... })在源模型上注册 Sequelize 的 HasMany 关联字段名as即前端看到的字段名。注意这里显式传入了constraints: false即 NocoBase 不在数据库层创建外键约束ON DELETE 行为改由应用层执行下文详述自动建索引为目标集合的外键字段添加索引tcoll.addIndex([this.options.foreignKey])保证通过外键反查父记录的性能登记引用关系调用database.referenceMap.addReference(this.reference(association))把「目标表.外键 → 源表.源键」的引用关系连同onDelete策略登记到全局ReferencesMap中支持排序的附加能力若字段配置了sortable会自动在目标集合上创建名为{foreignKey}Sort的隐藏排序字段type 为sort并将sortBy指向它从而支持关系区块内手动排序has-many-field.ts。此外若目标模型尚未加载完成Target不存在字段会进入 pending 列表延迟建立见database.addPendingField(this)。unbind()删除关系字段的清理行为删除一对多字段时unbind()has-many-field.ts会同步清理元数据从referenceMap移除引用、删除模型上的关联定义并刷新属性如果目标表中的外键字段并非用户显式创建还会一并从目标模型中移除该外键属性。ON DELETE 的应用层执行机制NocoBase 没有依赖数据库外键约束而是通过「引用映射 删除前完整性检查」在应用层实现 ON DELETE 语义。这一机制由两个模块构成。ReferencesMap全局引用登记册references-map.ts 中的ReferencesMap以目标表名为键登记所有指向该表的引用记录每条Reference包含源表、源字段外键、目标字段源键与onDelete策略。关键规则默认策略未显式指定onDelete时默认为NO ACTIONDEFAULT_ON_DELETE NO ACTION见 references-map.ts优先级机制引用分default与user两个优先级用户显式配置的onDelete会覆盖默认策略两条同为用户优先级的引用发生冲突时CASCADE覆盖SET NULL其余情况记录 warn 日志而不抛错references-map.ts。referentialIntegrityCheck删除时的实际处理当通过 NocoBase 的 Repository 删除一条记录时会触发 referential-integrity-check.ts 中的referentialIntegrityCheck()它遍历该记录所在表的全部引用按策略处理referential-integrity-check.tsON DELETE应用层行为NO ACTION跳过不做任何处理RESTRICT若存在关联子记录抛出RESTRICT错误拒绝删除CASCADE在事务内调用子表 Repository 的destroy()删除全部关联记录SET NULL在事务内调用子表 Repository 的update()将关联外键置为 nullhooks: false可以推断由于该检查走的是 Repository 层只有经 NocoBase 数据层发起的删除才会触发这些语义绕过框架直接操作数据库的删除不受此机制约束。这也是为什么文档强调关系字段的 ON DELETE 属于 NocoBase 保存的关系元数据层面的规则。一对多与多对一如何取舍在 NocoBase 中O2M 与 M2O 描述的是同一条关联的两端一对多字段配置在「一」的一方外键落在「多」的一方多对一字段则从「多」的一方指向「一」的一方。界面选择关系类型时默认按业务语义判断——如果当前记录只属于一个目标记录通常用多对一如果当前记录需要看到目标表中的多条记录以子表/区块形式展示通常用一对多。创建一对多字段时关联集合中会自动生成对应的多对一Many-to-one反向字段两端可以互相查询。相关资源原始文档一对多关系字段总览关系字段字段模型实现has-many-field.ts、belongs-to-field.ts引用完整性references-map.ts、referential-integrity-check.ts测试用例has-many-field.test.ts、has-many-repository.test.ts【免费下载链接】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),仅供参考