限制和循环
在表之间添加约束意味着在使用 sequelize.sync
时,表必须按照一定的顺序在数据库中创建。如果 Task
引用了 User
,则必须先创建 User
表,然后才能创建 Task
表。这有时会导致循环引用,其中 Sequelize 无法找到同步顺序。想象一下文档和版本的场景。一个文档可以有多个版本,并且为了方便起见,文档具有对其当前版本的引用。
¥Adding constraints between tables means that tables must be created in the database in a certain order, when using sequelize.sync
. If Task
has a reference to User
, the User
table must be created before the Task
table can be created. This can sometimes lead to circular references, where Sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has a reference to its current version.
const { Sequelize, Model, DataTypes } = require('sequelize');
class Document extends Model {}
Document.init(
{
author: DataTypes.STRING,
},
{ sequelize, modelName: 'document' },
);
class Version extends Model {}
Version.init(
{
timestamp: DataTypes.DATE,
},
{ sequelize, modelName: 'version' },
);
Document.hasMany(Version); // This adds documentId attribute to version
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
}); // This adds currentVersionId attribute to document
然而,不幸的是上面的代码将导致以下错误:
¥However, unfortunately the code above will result in the following error:
Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
为了缓解这种情况,我们可以将 constraints: false
传递给其中一个关联:
¥In order to alleviate that, we can pass constraints: false
to one of the associations:
Document.hasMany(Version);
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
constraints: false,
});
这将使我们能够正确同步表:
¥Which will allow us to sync the tables correctly:
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" (
"id" SERIAL,
"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")
);
无限制地强制执行外键引用
¥Enforcing a foreign key reference without constraints
有时你可能想要引用另一个表,而不添加任何约束或关联。在这种情况下,你可以手动将引用属性添加到架构定义中,并标记它们之间的关系。
¥Sometimes you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
class Trainer extends Model {}
Trainer.init(
{
firstName: Sequelize.STRING,
lastName: Sequelize.STRING,
},
{ sequelize, modelName: 'trainer' },
);
// Series will have a trainerId = Trainer.id foreign reference key
// after we call Trainer.hasMany(series)
class Series extends Model {}
Series.init(
{
title: Sequelize.STRING,
subTitle: Sequelize.STRING,
description: Sequelize.TEXT,
// Set FK relationship (hasMany) with `Trainer`
trainerId: {
type: DataTypes.INTEGER,
references: {
model: Trainer,
key: 'id',
},
},
},
{ sequelize, modelName: 'series' },
);
// Video will have seriesId = Series.id foreign reference key
// after we call Series.hasOne(Video)
class Video extends Model {}
Video.init(
{
title: Sequelize.STRING,
sequence: Sequelize.INTEGER,
description: Sequelize.TEXT,
// set relationship (hasOne) with `Series`
seriesId: {
type: DataTypes.INTEGER,
references: {
model: Series, // Can be both a string representing the table name or a Sequelize model
key: 'id',
},
},
},
{ sequelize, modelName: 'video' },
);
Series.hasOne(Video);
Trainer.hasMany(Series);