User.hasOne(Project)
之类的方法时,我们说 User
模型(函数被调用的模型)是 source 。 Project
模型(作为参数传递的模型)是 target 。一、一对一关联
一对一关联是通过单个外键连接的两个模型之间的关联。
1、BelongsTo:源model - 属于的意思
属于的意思:源属于目标,在源上添加目标外键
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 的主键值
2、外键
默认情况下,将从目标模型名称和目标主键名称生成 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
3、目标键
目标键是源模型上的外键列指向的目标模型上的列。 默认情况下,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
4、HasOne:目标model
有一个的意思,源有一个目标,在目标上添加源的外键
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。
在Sequelize 1:1关系中可以使用HasOne和BelongsTo进行设置。 它们适用于不同的场景。 让我们用一个例子来研究这个差异。假设我们有两个表可以链接 Player 和 Team 。 让我们定义他们的模型。
const Player = this.sequelize.define('player', {/* attributes */})
const Team = this.sequelize.define('team', {/* attributes */});
当我们连接 Sequelize 中的两个模型时,我们可以将它们称为一对 source 和 target 模型。像这样
将 Player 作为 source 而 Team 作为 target
Player.belongsTo(Team);
//或
Player.hasOne(Team);
将 Team 作为 source 而 Player 作为 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 模型中
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'})
这会将 projectId
或 project_id
属性添加到 User。Project 的实例将获得访问器 getWorkers
和 setWorkers
。
有时您可能需要在不同的列上关联记录,您可以使用 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 的新模型,具有等效的外键projectId
和userId
。 属性是否为camelcase
取决于由表(在这种情况下为User
和Project
)连接的两个模型。
定义 through
为 required。 Sequelize 以前会尝试自动生成名称,但并不总是导致最合乎逻辑的设置。
这将添加方法 getUsers
, setUsers
, addUser
,addUsers
到 Project
, 还有 getProjects
, setProjects
, addProject
, 和 addProjects
到 User
。
有时,您可能需要在关联中使用它们时重命名模型。 让我们通过使用别名(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' }})
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' })