User
模型(调用函数的模型)是 来源(Source).
Project
模型(作为参数传递的模型)是 目标(Target).
当你在模型中创建关联时,会自动创建带约束的外键引用. 下面是设置:
class Task extends Model {}
Task.init({ title: Sequelize.STRING }, { sequelize, modelName: 'task' });
class User extends Model {}
User.init({ username: Sequelize.STRING }, { sequelize, modelName: 'user' });
User.hasMany(Task); // 将userId添加到Task模型
Task.belongsTo(User); // 同样会将userId添加到Task模型中
将生成以下SQL:
CREATE TABLE IF NOT EXISTS "users" (
"id" SERIAL,
"username" VARCHAR(255),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tasks" (
"id" SERIAL,
"title" VARCHAR(255),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"userId" INTEGER REFERENCES "users" ("id") ON DELETE
SET
NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
对于 1:1 和 1:m 关联,默认选项是 SET NULL
用于删除,CASCADE
用于更新. 对于 n:m,两者的默认值是 CASCADE
. 这意味着,如果你从 n:m 关联的一侧删除或更新一行,则引用该行的连接表中的所有行也将被删除或更新.
underscored 选项
Sequelize 允许为 Model 设置 underscored
选项. 当 true
时,此选项会将所有属性的 field
参数设置为其名称的下划线版本. 这也适用于关联生成的外键.
让我们修改最后一个例子来使用 underscored
选项.
CREATE TABLE IF NOT EXISTS "users" (
"username" VARCHAR(255),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tasks" (
"id" SERIAL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"user_id" INTEGER REFERENCES "users" ("id") ON DELETE
SET
NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
注入模型的命名属性仍然为驼峰,只是 field
参数将其设置为下划线版本.
循环依赖关系 & 禁用约束
在表之间添加约束意味着在使用 sequelize.sync
时,必须以特定顺序在数据库中创建表. 如果Task
引用了User
,则必须在创建tasks
表之前创建users
表. 这有时会导致循环引用,其中 sequelize 无法找到要同步的顺序. 想象一下文档和版本的场景. 文档可以有多个版本,为方便起见,文档可以引用其当前版本.
class Document extends Model {}
Document.init({
author: Sequelize.STRING
}, { sequelize, modelName: 'document' });
class Version extends Model {}
Version.init({
timestamp: Sequelize.DATE
}, { sequelize, modelName: 'version' });
Document.hasMany(Version); // 这会将 documentId 属性添加到 version 中
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId'
}); // 这会将 currentVersionId 属性添加到 document 中
但是,上面的代码将导致以下错误: Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
.为了解决这一问题,我们向其中一个关联传递 constraints: false
:
CREATE TABLE IF NOT EXISTS "documents" (
"id" SERIAL,
"author" VARCHAR(255),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"currentVersionId" INTEGER,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "versions" (
"timestamp" TIMESTAMP WITH TIME ZONE,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
SET
NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
有时你可能想引用另一个表,而不添加任何约束或关联. 在这种情况下,你可以手动将参考属性添加到你的模式定义中,并标记它们之间的关系.
Trainer.init({
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
}, { sequelize, modelName: 'trainer' });
// 在我们调用 Trainer.hasMany(series) 后,
// series 将有一个 trainerId = Trainer.id 外键
class Series extends Model {}
Series.init({
title: Sequelize.STRING,
subTitle: Sequelize.STRING,
description: Sequelize.TEXT,
// 用 `Trainer` 设置 FK 关系(hasMany)
trainerId: {
type: Sequelize.INTEGER,
references: {
model: Trainer,
key: 'id'
}
}
}, { sequelize, modelName: 'series' });
// 在我们调用 Series.hasOne(Video) 之后
// video 将有 seriesId = Series.id 外键
class Video extends Model {}
Video.init({
title: Sequelize.STRING,
sequence: Sequelize.INTEGER,
description: Sequelize.TEXT,
// 用 `Series` 设置关系(hasOne)
seriesId: {
type: Sequelize.INTEGER,
references: {
model: Series, // 既可以是表示表名的字符串,也可以是 Sequelize 模型
key: 'id'
}
}
}, { sequelize, modelName: 'video' });