Sequelize 中文文件 v4 – Associations – 關聯

DemoPark發表於2019-02-16

Associations – 關聯

此係列文章的應用示例已釋出於 GitHub: sequelize-docs-Zh-CN. 可以 Fork 幫助改進或 Star 關注更新. 歡迎 Star.

Sequelize Docs 中文文件目錄

本部分描述 sequelize 中的各種關聯型別。 當呼叫 User.hasOne(Project) 這樣的方法時,我們說 User 模型(該函式被呼叫的模型)是 sourceProject 模型(模型被傳遞為引數)是 target

一對一關聯

一對一關聯是通過單個外來鍵連線的兩個模型之間的關聯。

BelongsTo

BelongsTo 關聯是在 source model 上存在一對一關係的外來鍵的關聯。

一個簡單的例子是 Player 通過 player 的外來鍵作為 Team 的一部分。

const Player = this.sequelize.define(`player`, {/* attributes */});
const Team  = this.sequelize.define(`team`, {/* attributes */});

Player.belongsTo(Team); // 將向 Team 新增一個 teamId 屬性以儲存 Team 的主鍵值

外來鍵

預設情況下,將從目標模型名稱和目標主鍵名稱生成 belongsTo 關係的外來鍵。

預設的樣式是 camelCase,但是如果源模型配置為 underscored: true ,那麼 foreignKey 將是snake_case

const User = this.sequelize.define(`user`, {/* attributes */})
const Company  = this.sequelize.define(`company`, {/* attributes */});

User.belongsTo(Company); // 將 companyId 新增到 user

const User = this.sequelize.define(`user`, {/* attributes */}, {underscored: true})
const Company  = this.sequelize.define(`company`, {
  uuid: {
    type: Sequelize.UUID,
    primaryKey: true
  }
});

User.belongsTo(Company); // 將 company_uuid 新增到 user

在已定義 as 的情況下,將使用它代替目標模型名稱。

const User = this.sequelize.define(`user`, {/* attributes */})
const UserRole  = this.sequelize.define(`userRole`, {/* attributes */});

User.belongsTo(UserRole, {as: `role`}); // 將 role 新增到 user 而不是 userRole

在所有情況下,預設外來鍵可以用 foreignKey 選項覆蓋。
當使用外來鍵選項時,Sequelize 將按原樣使用:

const User = this.sequelize.define(`user`, {/* attributes */})
const Company  = this.sequelize.define(`company`, {/* attributes */});

User.belongsTo(Company, {foreignKey: `fk_company`}); // 將 fk_company 新增到 User

目標鍵

目標鍵是源模型上的外來鍵列指向的目標模型上的列。 預設情況下,belongsTo 關係的目標鍵將是目標模型的主鍵。 要定義自定義列,請使用 targetKey 選項。

const User = this.sequelize.define(`user`, {/* attributes */})
const Company  = this.sequelize.define(`company`, {/* attributes */});

User.belongsTo(Company, {foreignKey: `fk_companyname`, targetKey: `name`}); // 新增 fk_companyname 到 User

HasOne

HasOne 關聯是在 target model 上存在一對一關係的外來鍵的關聯。

const User = sequelize.define(`user`, {/* ... */})
const Project = sequelize.define(`project`, {/* ... */})
 
// 單向關聯
Project.hasOne(User)

/*
  在此示例中,hasOne 將向 User 模型新增一個 projectId 屬性 ! 
  此外,Project.prototype 將根據傳遞給定義的第一個引數獲取 getUser 和 setUser 的方法。 
  如果啟用了 underscore 樣式,則新增的屬性將是 project_id 而不是 projectId。

  外來鍵將放在 users 表上。

  你也可以定義外來鍵,例如 如果您已經有一個現有的資料庫並且想要處理它:
*/
 
Project.hasOne(User, { foreignKey: `initiator_id` })
 
/*
  因為Sequelize將使用模型的名稱(define的第一個引數)作為訪問器方法,
  還可以將特殊選項傳遞給hasOne:
*/
 
Project.hasOne(User, { as: `Initiator` })
// 現在你可以獲得 Project#getInitiator 和 Project#setInitiator
 
// 或者讓我們來定義一些自己的參考
const Person = sequelize.define(`person`, { /* ... */})
 
Person.hasOne(Person, {as: `Father`})
// 這會將屬性 FatherId 新增到 Person
 
// also possible:
Person.hasOne(Person, {as: `Father`, foreignKey: `DadId`})
// 這將把屬性 DadId 新增到 Person
 
// 在這兩種情況下,你都可以:
Person#setFather
Person#getFather
 
// 如果你需要聯結表兩次,你可以聯結同一張表
Team.hasOne(Game, {as: `HomeTeam`, foreignKey : `homeTeamId`});
Team.hasOne(Game, {as: `AwayTeam`, foreignKey : `awayTeamId`});

Game.belongsTo(Team);

即使它被稱為 HasOne 關聯,對於大多數1:1關係,您通常需要BelongsTo關聯,因為 BelongsTo 將會在 hasOne 將新增到目標的源上新增 foreignKey。

HasOne 和 BelongsTo 之間的區別

在Sequelize 1:1關係中可以使用HasOne和BelongsTo進行設定。 它們適用於不同的場景。 讓我們用一個例子來研究這個差異。

假設我們有兩個表可以連結 PlayerTeam 。 讓我們定義他們的模型。

const Player = this.sequelize.define(`player`, {/* attributes */})
const Team  = this.sequelize.define(`team`, {/* attributes */});

當我們連線 Sequelize 中的兩個模型時,我們可以將它們稱為一對 sourcetarget 模型。像這樣

Player 作為 sourceTeam 作為 target

Player.belongsTo(Team);
//或
Player.hasOne(Team);

Team 作為 sourcePlayer 作為 target

Team.belongsTo(Player);
//Or
Team.hasOne(Player);

HasOne 和 BelongsTo 將關聯鍵插入到不同的模型中。 HasOne 在 target 模型中插入關聯鍵,而 BelongsTo 將關聯鍵插入到 source 模型中。

下是一個示例,說明了 BelongsTo 和 HasOne 的用法。

const Player = this.sequelize.define(`player`, {/* attributes */})
const Coach  = this.sequelize.define(`coach`, {/* attributes */})
const Team  = this.sequelize.define(`team`, {/* attributes */});

假設我們的 Player 模型有關於其團隊的資訊為 teamId 列。 關於每個團隊的 Coach 的資訊作為 coachId 列儲存在 Team 模型中。 這兩種情況都需要不同種類的1:1關係,因為外來鍵關係每次出現在不同的模型上。

當關於關聯的資訊存在於 source 模型中時,我們可以使用 belongsTo。 在這種情況下,Player 適用於 belongsTo,因為它具有 teamId 列。

Player.belongsTo(Team)  // `teamId` 將被新增到 Player / Source 模型中

當關於關聯的資訊存在於 target 模型中時,我們可以使用 hasOne。 在這種情況下, Coach 適用於 hasOne ,因為 Team 模型將其 Coach 的資訊儲存為 coachId 欄位。

Coach.hasOne(Team)  // `coachId` 將被新增到 Team / Target 模型中

一對多關聯

一對多關聯將一個來源與多個目標連線起來。 而多個目標接到同一個特定的源。

const User = sequelize.define(`user`, {/* ... */})
const Project = sequelize.define(`project`, {/* ... */})
 
// 好。 現在,事情變得更加複雜(對使用者來說並不真實可見)。
// 首先我們來定義一個 hasMany 關聯
Project.hasMany(User, {as: `Workers`})

這將新增屬性 projectIdproject_id 到 User。 Project 的例項將獲得訪問器 getWorkerssetWorkers。 我們讓它保持原樣,讓它成為單向關聯。
但是我們想要更多! 讓我們在下一節中以其他方式定義並建立一個多對多的關聯:

有時您可能需要在不同的列上關聯記錄,您可以使用 sourceKey 選項:

const City = sequelize.define(`city`, { countryCode: Sequelize.STRING });
const Country = sequelize.define(`country`, { isoCode: Sequelize.STRING });

// 在這裡,我們可以根據國家程式碼連線國家和城市
Country.hasMany(City, {foreignKey: `countryCode`, sourceKey: `isoCode`});
City.belongsTo(Country, {foreignKey: `countryCode`, targetKey: `isoCode`});

多對多關聯

多對多關聯用於將源與多個目標相連線。 此外,目標也可以連線到多個源。

Project.belongsToMany(User, {through: `UserProject`});
User.belongsToMany(Project, {through: `UserProject`});

這將建立一個名為 UserProject 的新模型,具有等效的外來鍵projectIduserId。 屬性是否為camelcase取決於由表(在這種情況下為UserProject)連線的兩個模型。

定義 throughrequired。 Sequelize 以前會嘗試自動生成名稱,但並不總是導致最合乎邏輯的設定。

這將新增方法 getUsers, setUsers, addUser,addUsersProject, 還有 getProjects, setProjects, addProject, 和 addProjectsUser.

有時,您可能需要在關聯中使用它們時重新命名模型。 讓我們通過使用別名(as)選項將 users 定義為 workers 而 projects 定義為t asks。 我們還將手動定義要使用的外來鍵:

User.belongsToMany(Project, { as: `Tasks`, through: `worker_tasks`, foreignKey: `userId` })
Project.belongsToMany(User, { as: `Workers`, through: `worker_tasks`, foreignKey: `projectId` })

foreignKey 將允許你在 through 關係中設定 source model 鍵。

otherKey 將允許你在 through 關係中設定 target model 鍵。

User.belongsToMany(Project, { as: `Tasks`, through: `worker_tasks`, foreignKey: `userId`, otherKey: `projectId`})

當然你也可以使用 belongsToMany 定義自我引用:

Person.belongsToMany(Person, { as: `Children`, through: `PersonChildren` })
// 這將建立儲存物件的 ID 的表 PersonChildren。

如果您想要連線表中的其他屬性,則可以在定義關聯之前為連線表定義一個模型,然後再說明它應該使用該模型進行連線,而不是建立一個新的關聯:

const User = sequelize.define(`user`, {})
const Project = sequelize.define(`project`, {})
const UserProjects = sequelize.define(`userProjects`, {
    status: DataTypes.STRING
})
 
User.belongsToMany(Project, { through: UserProjects })
Project.belongsToMany(User, { through: UserProjects })

要向 user 新增一個新 project 並設定其狀態,您可以將額外的 options.through 傳遞給 setter,其中包含連線表的屬性

user.addProject(project, { through: { status: `started` }})

預設情況下,上面的程式碼會將 projectId 和 userId 新增到 UserProjects 表中, 刪除任何先前定義的主鍵屬性 – 表將由兩個表的鍵的組合唯一標識,並且沒有其他主鍵列。 要在 UserProjects 模型上強新增一個主鍵,您可以手動新增它。

const UserProjects = sequelize.define(`userProjects`, {
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  status: DataTypes.STRING
})

使用多對多你可以基於 through 關係查詢並選擇特定屬性。 例如通過 through 使用findAll

User.findAll({
  include: [{
    model: Project,
    through: {
      attributes: [`createdAt`, `startedAt`, `finishedAt`],
      where: {completed: true}
    }
  }]
});

作用域

本節涉及關聯作用域。 有關關聯作用域與相關模型上的作用域的定義,請參閱 作用域

關聯作用域允許您在關聯上放置一個作用域(一套 getcreate 的預設屬性)。作用域可以放在相關聯的模型(關聯的target)上,也可以通過表上的 n:m 關係。

1:m

假設我們有表評論,帖子和影像。 一個評論可以通過 commentable_idcommentable 關聯到一個影像或一個帖子 – 我們說 Post 和 Image 是 Commentable

const Comment = this.sequelize.define(`comment`, {
  title: Sequelize.STRING,
  commentable: Sequelize.STRING,
  commentable_id: Sequelize.INTEGER
});

Comment.prototype.getItem = function(options) {
  return this[`get` + this.get(`commentable`).substr(0, 1).toUpperCase() + this.get(`commentable`).substr(1)](options);
};

Post.hasMany(this.Comment, {
  foreignKey: `commentable_id`,
  constraints: false,
  scope: {
    commentable: `post`
  }
});
Comment.belongsTo(this.Post, {
  foreignKey: `commentable_id`,
  constraints: false,
  as: `post`
});

Image.hasMany(this.Comment, {
  foreignKey: `commentable_id`,
  constraints: false,
  scope: {
    commentable: `image`
  }
});
Comment.belongsTo(this.Image, {
  foreignKey: `commentable_id`,
  constraints: false,
  as: `image`
});

constraints: false, 禁用引用約束 – 由於 commentable_id 列引用了幾個表,我們不能新增一個 REFERENCES 約束。 請注意,Image – > Comment 和 Post – > Comment 關係分別定義了一個作用域:commentable: `image`commentable: `post`。 使用關聯功能時自動應用此作用域:

image.getComments()
SELECT * FROM comments WHERE commentable_id = 42 AND commentable = `image`;

image.createComment({
  title: `Awesome!`
})
INSERT INTO comments (title, commentable_id, commentable) VALUES (`Awesome!`, 42, `image`);

image.addComment(comment);
UPDATE comments SET commentable_id = 42, commentable = `image`

Comment 上的 getItem 作用函式完成了圖片 – 它只是將commentable字串轉換為getImagegetPost的一個呼叫,提供一個註釋是屬於一個帖子還是一個影像的抽象概念。您可以將普通選項物件作為引數傳遞給 getItem(options),以指定任何條件或包含的位置。

n:m

繼續多型模型的思路,考慮一個 tag 表 – 一個 item 可以有多個 tag,一個 tag 可以與多個 item 相關。

為了簡潔起見,該示例僅顯示了 Post 模型,但實際上 Tag 與其他幾個模型相關。

const ItemTag = sequelize.define(`item_tag`, {
  id : {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  tag_id: {
    type: DataTypes.INTEGER,
    unique: `item_tag_taggable`
  },
  taggable: {
    type: DataTypes.STRING,
    unique: `item_tag_taggable`
  },
  taggable_id: {
    type: DataTypes.INTEGER,
    unique: `item_tag_taggable`,
    references: null
  }
});
const Tag = sequelize.define(`tag`, {
  name: DataTypes.STRING
});

Post.belongsToMany(Tag, {
  through: {
    model: ItemTag,
    unique: false,
    scope: {
      taggable: `post`
    }
  },
  foreignKey: `taggable_id`,
  constraints: false
});
Tag.belongsToMany(Post, {
  through: {
    model: ItemTag,
    unique: false
  },
  foreignKey: `tag_id`,
  constraints: false
});

請注意,作用域列(taggable)現在在 through 模型(ItemTag)上。

我們還可以定義一個更具限制性的關聯,例如,通過應用through 模型(ItemTag)和目標模型(Tag)的作用域來獲取所有掛起的 tag。

Post.hasMany(Tag, {
  through: {
    model: ItemTag,
    unique: false,
    scope: {
      taggable: `post`
    }
  },
  scope: {
    status: `pending`
  },
  as: `pendingTags`,
  foreignKey: `taggable_id`,
  constraints: false
});

Post.getPendingTags();
SELECT `tag`.*  INNER JOIN `item_tags` AS `item_tag`
ON `tag`.`id` = `item_tag`.`tagId`
  AND `item_tag`.`taggable_id` = 42
  AND `item_tag`.`taggable` = `post`
WHERE (`tag`.`status` = `pending`);

constraints: false 禁用 taggable_id 列上的引用約束。 因為列是多型的,我們不能說它是 REFERENCES 一個特定的表。

命名策略

預設情況下,Sequelize將使用模型名稱(傳遞給sequelize.define的名稱),以便在關聯時使用模型名稱。 例如,一個名為user的模型會將關聯模型的例項中的get / set / add User函式和加入一個名為.user的屬性,而一個名為User的模型會新增相同的功能,和一個名為.User的屬性(注意大寫U)。

正如我們已經看到的,你可以使用as來關聯模型。 在單個關聯(has one 和 belongs to),別名應該是單數,而對於許多關聯(has many)它應該是複數。 Sequelize然後使用[inflection] [0]庫將別名轉換為其單數形式。 但是,這可能並不總是適用於不規則或非英語單詞。 在這種情況下,您可以提供複數和單數形式的別名:

User.belongsToMany(Project, { as: { singular: `task`, plural: `tasks` }})
// Notice that inflection has no problem singularizing tasks, this is just for illustrative purposes.

如果你知道模型將始終在關聯中使用相同的別名,則可以在建立模型時提供它

const Project = sequelize.define(`project`, attributes, {
  name: {
    singular: `task`,
    plural: `tasks`,
  }
})
 
User.belongsToMany(Project);

這將為使用者例項新增 add/set/get Tasks 方法。

記住,使用as來更改關聯的名稱也會改變外來鍵的名稱。 當使用as時,也可以指定外來鍵是最安全的。

Invoice.belongsTo(Subscription)
Subscription.hasMany(Invoice)

不使用 as,這會按預期新增 subscriptionId。 但是,如果您要傳送Invoice.belongsTo(Subscription, { as: `TheSubscription` }),那麼您將同時擁有 subscriptionIdtheSubscriptionId,因為 sequelize 不夠聰明,無法確定呼叫是相同關係的兩面。 foreignKey 修正了這個問題;

Invoice.belongsTo(Subscription, , { as: `TheSubscription`, foreignKey: `subscription_id` })
Subscription.hasMany(Invoice, { foreignKey: `subscription_id` )

關聯物件

因為 Sequelize 做了很多神奇的事,所以你必須在設定關聯後呼叫 Sequelize.sync。 這樣做將允許您進行以下操作:

Project.belongsToMany(Task)
Task.belongsToMany(Project)
 
Project.create()...
Task.create()...
Task.create()...
 
// 儲存它們.. 然後:
project.setTasks([task1, task2]).then(() => {
  // 已儲存!
})
 
// 好的,現在它們已經儲存了...我怎麼才能得到他們?
project.getTasks().then(associatedTasks => {
  // associatedTasks 是一個 tasks 的陣列
})
 
// 您還可以將過濾器傳遞給getter方法。
// 它們與你能傳遞給常規查詢器方法的選項相同。
project.getTasks({ where: `id > 10` }).then(tasks => {
  // id大於10的任務
})
 
// 你也可以僅檢索關聯物件的某些欄位。
project.getTasks({attributes: [`title`]}).then(tasks => {
  // 使用屬性“title”和“id”檢索任務
})

要刪除建立的關聯,您可以呼叫set方法而不使用特定的ID:

// 刪除與 task1 的關聯
project.setTasks([task2]).then(associatedTasks => {
  // 你將只得到 task2
})
 
// 刪除全部
project.setTasks([]).then(associatedTasks => {
  // 你將得到空陣列
})
 
// 或更直接地刪除
project.removeTask(task1).then(() => {
  // 什麼都沒有
})
 
// 然後再次新增它們
project.addTask(task1).then(function() {
  // 它們又回來了
})

反之亦然你當然也可以這樣做:

// project與task1和task2相關聯
task2.setProject(null).then(function() {
  // 什麼都沒有
})

對於 hasOne/belongsTo 與其基本相同:

Task.hasOne(User, {as: "Author"})
Task#setAuthor(anAuthor)

可以通過兩種方式新增與自定義連線表的關係的關聯(繼續前一章中定義的關聯):

// 在建立關聯之前,通過向物件新增具有連線表模型名稱的屬性
project.UserProjects = {
  status: `active`
}
u.addProject(project)
 
// 或者在新增關聯時提供第二個options.through引數,其中包含應該在連線表中的資料
u.addProject(project, { through: { status: `active` }})
 
 
// 關聯多個物件時,可以組合上述兩個選項。 在這種情況下第二個引數
// 如果沒有提供使用的資料將被視為預設物件
project1.UserProjects = {
    status: `inactive`
}
 
u.setProjects([project1, project2], { through: { status: `active` }})
// 上述程式碼將對專案1記錄無效,並且在連線表中對專案2進行active

當獲取具有自定義連線表的關聯的資料時,連線表中的資料將作為DAO例項返回:

u.getProjects().then(projects => {
  const project = projects[0]
 
  if (project.UserProjects.status === `active`) {
    // .. 做點什麼
 
    // 由於這是一個真正的DAO例項,您可以在完成操作之後直接儲存它
    return project.UserProjects.save()
  }
})

如果您僅需要連線表中的某些屬性,則可以提供具有所需屬性的陣列:

// 這將僅從 Projects 表中選擇 name,僅從 UserProjects 表中選擇status
user.getProjects({ attributes: [`name`], joinTableAttributes: [`status`]})

檢查關聯

您還可以檢查物件是否已經與另一個物件相關聯(僅 n:m)。 這是你怎麼做的

// 檢查物件是否是關聯物件之一:
Project.create({ /* */ }).then(project => {
  return User.create({ /* */ }).then(user => {
    return project.hasUser(user).then(result => {
      // 結果是 false
      return project.addUser(user).then(() => {
        return project.hasUser(user).then(result => {
          // 結果是 true
        })
      })
    })
  })
})
 
// 檢查所有關聯的物件是否如預期的那樣:
// 我們假設我們已經有一個專案和兩個使用者
project.setUsers([user1, user2]).then(() => {
  return project.hasUsers([user1]);
}).then(result => {
  // 結果是 false
  return project.hasUsers([user1, user2]);
}).then(result => {
  // 結果是 true
})

外來鍵

當您在sequelize模型中建立關聯時,將自動建立具有約束的外來鍵引用。 設定如下:

const Task = this.sequelize.define(`task`, { title: Sequelize.STRING })
const User = this.sequelize.define(`user`, { username: Sequelize.STRING })
 
User.hasMany(Task)
Task.belongsTo(User)

將生成以下SQL:

CREATE TABLE IF NOT EXISTS `User` (
  `id` INTEGER PRIMARY KEY,
  `username` VARCHAR(255)
);

CREATE TABLE IF NOT EXISTS `Task` (
  `id` INTEGER PRIMARY KEY,
  `title` VARCHAR(255),
  `user_id` INTEGER REFERENCES `User` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);

在task和user的關係之 中在task上注入user_id外來鍵,並將其標記為User表的引用。預設情況下,如果引用的使用者被刪除,user_id將被設定為NULL,如果更新了使用者標識的id,則會被更新。通過將onUpdateonDelete選項傳遞給關聯呼叫,可以覆蓋這些選項。驗證選項為RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL

對於1:1和1:m關聯,預設選項為SET NULL用於刪除, CASCADE用於更新。對於n:m,兩者的預設值為CASCADE。這意味著,如果從n:m關聯的一側刪除或更新行,引用該行的連線表中的所有行也將被刪除或更新。

在表之間新增約束意味著在使用sequelize.sync時,必須以特定順序在資料庫中建立表。如果Task引用了User,則必須先建立User表,然後才能建立Task表。這有時可能導致迴圈引用,其中後遺症找不到要同步的順序。想象一下檔案和版本的場景。一個文件可以有多個版本,為方便起見,一個文件可以引用它的當前版本。

const Document = this.sequelize.define(`document`, {
  author: Sequelize.STRING
})
const Version = this.sequelize.define(`version`, {
  timestamp: Sequelize.DATE
})

Document.hasMany(Version) // 這將 document_id 新增到版本
Document.belongsTo(Version, { as: `Current`, foreignKey: `current_version_id`}) // 這將current_version_id新增到文件

但是,上面的程式碼將導致以下錯誤: Cyclic dependency found. `Document` is dependent of itself. Dependency Chain: Document -> Version => Document.
為了減輕這一點,我們可以將 constraints: false 傳遞給其中一個關聯:

Document.hasMany(Version)
Document.belongsTo(Version, { as: `Current`, foreignKey: `current_version_id`, constraints: false})

這將允許我們正確地同步表:

CREATE TABLE IF NOT EXISTS `Document` (
  `id` INTEGER PRIMARY KEY,
  `author` VARCHAR(255),
  `current_version_id` INTEGER
);
CREATE TABLE IF NOT EXISTS `Version` (
  `id` INTEGER PRIMARY KEY,
  `timestamp` DATETIME,
  `document_id` INTEGER REFERENCES `Document` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);

強制執行外來鍵引用而不受約束

有時,您可能需要引用另一個表,而不新增任何約束或關聯。 在這種情況下,您可以手動將引用屬性新增到模式定義,並標記它們之間的關係。

// 在我們呼叫 Trainer.hasMany(series) 之後 Series 有一個 外參考鍵 trainer_id=Trainer.id 
const Series = sequelize.define(`series`, {
  title:        DataTypes.STRING,
  sub_title:    DataTypes.STRING,
  description:  DataTypes.TEXT,
 
  // 用 `Trainer` 設定外來鍵關係(hasMany) 
  trainer_id: {
    type: DataTypes.INTEGER,
    references: {
      model: "trainers",
      key: "id"
    }
  }
})
 
const Trainer = sequelize.define(`trainer`, {
  first_name: DataTypes.STRING,
  last_name:  DataTypes.STRING
});
 
// 在我們呼叫 Series.hasOne(Video) 之後 Video 有一個 外參考鍵 series_id=Series.id
const Video = sequelize.define(`video`, {
  title:        DataTypes.STRING,
  sequence:     DataTypes.INTEGER,
  description:  DataTypes.TEXT,
 
  // 用 `Series` 設定關係(hasOne) 
  series_id: {
    type: DataTypes.INTEGER,
    references: {
      model: Series, // 可以是表示表名稱的字串,也可以是對模型的引用
      key:   "id"
    }
  }
});
 
Series.hasOne(Video);
Trainer.hasMany(Series);

用關聯建立

如果所有元素都是新的,則可以在一個步驟中建立具有巢狀關聯的例項。

建立一個 “BelongsTo”, “Has Many” 或 “HasOne” 關聯的元素

考慮以下模型:

const Product = this.sequelize.define(`product`, {
  title: Sequelize.STRING
});
const User = this.sequelize.define(`user`, {
  first_name: Sequelize.STRING,
  last_name: Sequelize.STRING
});
const Address = this.sequelize.define(`address`, {
  type: Sequelize.STRING,
  line_1: Sequelize.STRING,
  line_2: Sequelize.STRING,
  city: Sequelize.STRING,
  state: Sequelize.STRING,
  zip: Sequelize.STRING,
});

Product.User = Product.belongsTo(User);
User.Addresses = User.hasMany(Address);
// 也能用於 `hasOne`

可以通過以下方式在一個步驟中建立一個新的Product, User和一個或多個Address:

return Product.create({
  title: `Chair`,
  user: {
    first_name: `Mick`,
    last_name: `Broadstone`,
    addresses: [{
      type: `home`,
      line_1: `100 Main St.`,
      city: `Austin`,
      state: `TX`,
      zip: `78704`
    }]
  }
}, {
  include: [{
    association: Product.User,
    include: [ User.Addresses ]
  }]
});

這裡,我們的使用者模型稱為user,帶小寫u – 這意味著物件中的屬性也應該是user。 如果給sequelize.define指定的名稱為User,物件中的鍵也應為User。 對於addresses也是同樣的,除了它是一個 hasMany 關聯的複數。

用別名建立一個 “BelongsTo” 關聯的元素

可以將前面的示例擴充套件為支援關聯別名。

const Creator = Product.belongsTo(User, {as: `creator`});

return Product.create({
  title: `Chair`,
  creator: {
    first_name: `Matt`,
    last_name: `Hansen`
  }
}, {
  include: [ Creator ]
});

建立 “HasMany” 或 “BelongsToMany” 關聯的元素

我們來介紹將產品與許多標籤相關聯的功能。 設定模型可能如下所示:

const Tag = this.sequelize.define(`tag`, {
  name: Sequelize.STRING
});

Product.hasMany(Tag);
// Also works for `belongsToMany`.

現在,我們可以通過以下方式建立具有多個標籤的產品:

Product.create({
  id: 1,
  title: `Chair`,
  tags: [
    { name: `Alpha`},
    { name: `Beta`}
  ]
}, {
  include: [ Tag ]
})

然後,我們可以修改此示例以支援別名:

const Categories = Product.hasMany(Tag, {as: `categories`});

Product.create({
  id: 1,
  title: `Chair`,
  categories: [
    {id: 1, name: `Alpha`},
    {id: 2, name: `Beta`}
  ]
}, {
  include: [{
    model: Categories,
    as: `categories`
  }]
})

如果這篇文章對您有幫助, 感謝 下方點贊 或 Star GitHub: sequelize-docs-Zh-CN 支援, 謝謝.


相關文章