Skip to content

Latest commit

 

History

History
executable file
·
880 lines (702 loc) · 27.6 KB

associations.md

File metadata and controls

executable file
·
880 lines (702 loc) · 27.6 KB

Associations - 关联

本部分描述了 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); // 将向 Player 添加一个 teamId 属性以保存 Team 的主键值

外键

默认情况下,将从目标模型名称和目标主键名称生成 belongsTo 关系的外键。

默认的样式是 camelCase,但是如果源模型配置为 underscored: true ,那么将使用字段 snake_case 创建 foreignKey。

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 模型中

一对多关联 (hasMany)

一对多关联将一个来源与多个目标连接起来。 而多个目标接到同一个特定的源。

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

当通过模型不存在主键时,Belongs-To-Many会创建唯一键。 可以使用 uniqueKey 选项覆盖此唯一键名。

Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })

作用域

本节涉及关联作用域。 有关关联作用域与关联模型作用域的定义, 请参阅 作用域

关联作用域允许您在关联上放置作用域(getcreate的一组默认属性)。 作用域可以放在关联模型(关联的目标)上,也可以放在n:m关系的中间表上。

1:m

假设我们有表 Comment,Post 和 Image。 Comment可以通过 commentable_idcommentable 与Image或Post相关联 - 我们称 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的调用,提供了 comment 是属于post还是image的抽象。 您可以将普通选项对象作为参数传递给getItem(options) 以指定 where 条件或包含的任何位置。

n:m

继续使用多态模型的概念,考虑一个标签表 - 一个项目可以有多个标签,标签可以与多个项目相关联。

为简洁起见,该示例仅显示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)现在位于中间模型(ItemTag)上。

我们还可以定义一个更具限制性的关联,例如,通过应用中间模型(ItemTag)和目标模型(Tag)的作用域来获取post的所有待定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.hasMany(Task)
Task.belongsTo(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 => {
  // 结果是 true
  return project.hasUsers([user1, user2]);
}).then(result => {
  // 结果是 true
})

外键

当您在模型中创建关联时,会自动创建带约束的外键引用。 下面是设置:

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

tasksusers 模型之间的关系通过在 tasks 表上注入 userId 外键,并将其标记为对 users 表的引用。 默认情况下,如果引用的用户被删除,userId 将被设置为 NULL,如果更新了 userId,则更新 userId。 这些选项可以通过将 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表。 这有时会导致循环引用,其中sequelize无法找到要同步的顺序。 想象一下文档和版本的场景。 文档可以有多个版本,为方便起见,文档可以引用其当前版本。

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

Document.hasMany(Version) // 这将 document_id 属性添加到 version
Document.belongsTo(Version, { as: 'Current', foreignKey: 'current_version_id'}) // 这将 current_version_id 属性添加到 document

但是,上面的代码将导致以下错误: 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
);

在没有约束的情况下强制执行外键引用

有时您可能想引用另一个表,而不添加任何约束或关联。 在这种情况下,您可以手动将参考属性添加到您的模式定义中,并标记它们之间的关系。

// Series 将有一个 trainerId = Trainer.id 外参考键
// 之后我们调用 Trainer.hasMany(series)
const Series = sequelize.define('series', {
  title:        DataTypes.STRING,
  sub_title:    DataTypes.STRING,
  description:  DataTypes.TEXT,
 
  // 用 `Trainer` 设置外键关系(hasMany)
  trainer_id: {
    type: DataTypes.INTEGER,
    references: {
      model: "trainer",
      key: "id"
    }
  }
})
 
const Trainer = sequelize.define('trainer', {
  first_name: DataTypes.STRING,
  last_name:  DataTypes.STRING
});
 
// Video 将有 seriesId = Series.id 外参考键
// 之后我们调用 Series.hasOne(Video)
const Video = sequelize.define('video', {
  title:        DataTypes.STRING,
  sequence:     DataTypes.INTEGER,
  description:  DataTypes.TEXT,
 
  // set relationship (hasOne) with `Series`
  series_id: {
    type: DataTypes.INTEGER,
    references: {
      model: Series, // 既可以是表示表名的字符串,也可以是 Sequelize 模型
      key:   "id"
    }
  }
});
 
Series.hasOne(Video);
Trainer.hasMany(Series);

用关联创建

如果所有元素都是新的,则可以在一个步骤中创建具有嵌套关联的实例。

BelongsTo / HasMany / 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'
  }]
})