メインコンテンツにスキップ
バージョン: v6 - 安定版

制約と循環参照

テーブル間に制約を追加するということは、`sequelize.sync` を使用する場合、テーブルをデータベースに特定の順序で作成する必要があることを意味します。 `Task` が `User` を参照している場合、`Task` テーブルを作成する前に `User` テーブルを作成する必要があります。これは、Sequelize が同期する順序を見つけられない循環参照につながることがあります。ドキュメントとバージョンのシナリオを想像してみてください。ドキュメントは複数のバージョンを持つことができ、便宜上、ドキュメントは現在のバージョンへの参照を持ちます。

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

しかし、残念ながら上記のコードは次のエラーになります

Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents

これを軽減するために、アソシエーションのいずれかに `constraints: false` を渡すことができます

Document.hasMany(Version);
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
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" (
"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")
);

制約なしで外部キー参照を強制する

制約やアソシエーションを追加せずに、別のテーブルを参照したい場合があります。その場合は、スキーマ定義に参照属性を手動で追加し、それらの間の関係をマークできます。

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);