间的关系关联
模型(Model)之间存在各种各样的关系,如:⼀对⼀(One-To-One )、⼀对多(One-To-Many)等。模型间的关系本质上是对其代表的数据库中表之间的关系描述,通过这些关系可以实现数据库中表之间主/外键约束的创建。查询时也可以基于这些关系,⽣成在数据库中执⾏的连接查询或复合查询SQL语句。
1. 关系/关联的使⽤
Sequelize 中的模型存在多种关系。在⼀个User.hasOne(Project)形式的调⽤中,正在调⽤的模型User是源模型⽽做为参数被传⼊的模型是⽬标模型。
1.1 ⼀对⼀(One-To-One)关联
⼀对⼀关联是由⼀个单⼀的外键,实现两个模型之间的精确关联。
BelongsTo - 属于
BelongsTo关联表⽰⼀对⼀关系的外键存在于源模型。
如,下例中Player是通过外键关联的Team的⼀部分:
var Player = this.sequelize.define('player', {/* attributes */})
, Team = this.sequelize.define('team', {/* attributes */});
Player.belongsTo(Team); // 会为Player添加⼀个teamId 属性以保持与Team 主键的关系
外键
默认情况下,⼀个属于关系的外键将从⽬标模型的名称和主键名称⽣成。
默认命名使⽤驼峰式命名,⽽在源模型中添加了underscored: true配置,将使⽤蛇型命名。
var User = this.sequelize.define('user', {/* attributes */})
, Company = this.sequelize.define('company', {/* attributes */});
User.belongsTo(Company); // 会为user 添加 companyId 属性
var User = this.sequelize.define('user', {/* attributes */}, {underscored: true})
,
Company = this.sequelize.define('company', {
uuid: {
type: Sequelize.UUID,
primaryKey: true
}
});
User.belongsTo(Company); // 会为user 添加 company_uuid 属性
在定义中使⽤as命名时,会将其做为⽬标模型的名称:
var User = this.sequelize.define('user', {/* attributes */})
, UserRole = this.sequelize.define('userRole', {/* attributes */});
User.belongsTo(UserRole, {as: 'role'}); // 会为 user添加 roleId 属性⽽不是 userRoleId
在任命情况下,使⽤使⽤了foreignKey选项,外键名都会使⽤此选项值。我可以在Sequelize 中像下⾯这样使⽤外键:
var User = this.sequelize.define('user', {/* attributes */})
北京现代 悦动, Company = this.sequelize.define('company', {/* attributes */});
User.belongsTo(Company, {foreignKey: 'fk_company'}); // 为User 添加fk_company 外键
⽬标键
⽬标键是位于⽬标模型上通过源模型外键列指向的列。默认情况下,⽬标键是会belongsTo关系中⽬标模型的主键。要使⽤⾃定义列,请⽤targetKey选项来指定:
var User = this.sequelize.define('user', {/* attributes */})
, Company = this.sequelize.define('company', {/* attributes */});
User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // 为User 添加 fk_companyname ⽬标键
HasOne - 拥有⼀个
HasOne关联表⽰⼀对⼀关系的外键存在于⽬标模型。
var User = sequelize.define('user', {/* ... */})
var Project = sequelize.define('project', {/* ... */})
// hasOne 关系
Project.hasOne(User)
/*
在这个⽰例中,hasOne会添加⼀个projectId 属性到User模型中
另外,Project.prototype 中会增加根据传⼊的第⼀个定义参数⽣成的访问器⽅法 getUser 和 setUser 置。
如果启⽤了underscore 设置,添加的属性会是 project_id ⽽不是 projectId.
外键会存在于users 表中
你同样可以⾃定义外键,如:你想使⽤⼀个已存在数据库:
*/
Project.hasOne(User, { foreignKey: 'initiator_id' })
/*
因为 Sequelize 会访问器使⽤模型名(定义时的第⼀个参数),
如果不使⽤这个名称,可以在hasOne 的选项中指定:
*/
Project.hasOne(User, { as: 'Initiator' })
// 这时会有 Project#getInitiator 和 Project#setInitiator
// 或者可以定义⼀些⾃引⽤
var Person = sequelize.define('person', { /* ... */})
Person.hasOne(Person, {as: 'Father'})
// 会为Person 增加⼀个FatherId 属性
// 同样可以⾃定义外键:
Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
// 会为Person 增加⼀个 DadId 属性
// 这两种情况下都会有以下两个⽅法:
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 则会在⽬标模型中添加外键。
HasOne 与BelongsTo 的不同
在1:1 的关系中,可使⽤HasOne 或BelongsTo来定义。但它们的使⽤场景有所不同,下⾯通过⼀个例⼦来说明。
我们有两张表,会别关联到Player 和Team模型,定义如下:
var Player = this.sequelize.define('player', {/* attributes */})
, Team = this.sequelize.define('team', {/* attributes */});
在Sequelize 中我们可以源和⽬标模型的形式将⼆者建⽴联系。如将Player 做为源模型,⽽将Team做为⽬标模型:
Player.belongsTo(Team);
/
/Or
Player.hasOne(Team);
或Team 做为源模型,⽽将Player做为⽬标模型:
Team.belongsTo(Player);
五菱宏光颜//Or
Team.hasOne(Player);
HasOne 和 BelongsTo 插⼊外键的位置有所不同。HasOne 会向⽬标模型中插⼊关联键,⽽BelongsTo 会向源模型中插⼊关联键。
⼀个演⽰HasOne 和 BelongsTo使⽤的⽰例:
var Player = this.sequelize.define('player', {/* attributes */})
, Coach = this.sequelize.define('coach', {/* attributes */})
, Team = this.sequelize.define('team', {/* attributes */});
假设Player模型通过teamId列与其团队建⽴联系,每个团队的教练Coach信息通过coachId列存储。在这些 1:1 场景中需要以不同的⽅式建⽴关系,因为模型外键的存储位置不同。
当信息关联是存在于当前源模型时,我们可以使⽤belongsTo。在上⾯⽰例中,Player适合使⽤belongsTo,因为它有teamId列。
Player.belongsTo(Team) // `teamId` 会添加到 Player / 源模型
当信息关联是存在于当前⽬标模型时,我们可以使⽤hasOne。在上⾯⽰例中,Coach适合使⽤hasOne,因为model模型中存储了它的Coach信息的coachId字段。
Coach.hasOne(Team) // `coachId` 会添加到 Team / ⽬标模型
1.2 ⼀对多(One-To-Many)关联
One-To-Many关联是指⼀个源模型连接多个⽬标模型。反之⽬标模型都会有⼀个明确的源。
var User = sequelize.define('user', {/* ... */})
var Project = sequelize.define('project', {/* ... */})
/
/ 定义 hasMany 关联
Project.hasMany(User, {as: 'Workers'})
会向 User 中添加⼀个projectId或project_id属性。Project 的实例中会有访问器getWorkers 和 setWorkers。这是⼀种单向关联⽅式,如果两个模型间还有其它关联⽅式请参考下⾯的多对多关系。
1.3 多对多(Belongs-To-Many)关联
Belongs-To-Many 关联是指⼀个源模型连接多个⽬标模型。⽽且,⽬标模型也可以有多个相关的源。
Project.belongsToMany(User, {through: 'UserProject'});
User.belongsToMany(Project, {through: 'UserProject'});
这会创建⼀个新模型UserProject其中会projectId和userId两个外键。是否使⽤驼峰命名取决与相关联的两个表。
定义through选项后,Sequelize会尝试⾃动⽣成名字,但并⼀定符合逻辑。
在本例中,会为User添加⽅法 getUsers, setUsers, addUser,addUsers to Project, and getProjects, se
tProjects, addProject, and addProjects
有时我们会对连接的模型进⾏重命名,同样可以使⽤as实现。如,将User 命名为Workers ,将Project 命名为 Tasks:
User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId' })
Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks', foreignKey: 'projectId' })
foreignKey让你可以设置through关系中的源模型,⽽otherKey让你可以through关系中的⽬标模型。
User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId', otherKey: 'projectId'})
belongsToMany同样可以⽤来定义⾃连接:
Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
// 这会创建 PersonChildren 表,其中存储了对象的 id
如果想为连接表添加更多属性,可以建⽴关联前在sequelize 中为连接表定义⼀个模型,这会告诉seq
uelize 使⽤这个表建⽴关联⽽不是创建新表:
User = sequelize.define('user', {})
Project = sequelize.define('project', {})
UserProjects = sequelize.define('userProjects', {
status: DataTypes.STRING
})
起亚k3销量User.belongsToMany(Project, { through: UserProjects })
Project.belongsToMany(User, { through: UserProjects })
要为user 添加⼀个新的project 并设置状态,可以在设置器中传⼊⼀个额外的对象,这个属性会包含在连接表中:
user.addProject(project, { status: 'started' })
默认情况下会向UserProjects表中添加projectId 和 userId,并移除之前已定义的主键属性表并由两个表的主键组建唯⼀标识,且没有额外的PK 列。要强制为UserProjects添加主键可以⼿⼯添加:
UserProjects = sequelize.define('userProjects', {
电动车质量id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
})
Belongs-To-Many 可以基于through关系查询并可以选择查询属性:
User.findAll({
include: [{
model: Project,
through: {
attributes: ['createdAt', 'startedAt', 'finishedAt'],
where: {completed: true}
电动续航里程}
}]
});
1.4 Scopes - 作⽤域
关系作⽤域允许你设置⼀个作⽤范围(⼀组⽤于设置和创建的默认属性)。作⽤域同样也以放在通过 n:m 建⽴关系的表的关联模型(关联⽬标)中。北京车管所网站
1:m
假设有Comment, Post 和 Image三个表,Comment表可以通过commentable_id和commentable字段分别关联image 或 post 表关联:
this.Comment = this.sequelize.define('comment', {
title: Sequelize.STRING,
commentable: Sequelize.STRING,
commentable_id: Sequelize.INTEGER
}, {
instanceMethods: {
发布评论