属于
声明属于关系
声明是属于关系的方法是 belongsTo
Book.belongsTo(User);
Book
是主体发起模型,而 User
是目标模型。这样声明会在 Book 上面增加 setUser
/ createUser
/ User
俩个方法与一个属性,通过 options 里面的 as: 'Some'
,来让名称变成 setSome
/ createUser
/ Some
。且在 Book 表里面会自动增加一个 UserId
的字段来记录 User 的 id 值,通过设置 options 里面的 foreignKey: 'user_id'
,来修改自动增加字段的名称。
倘若你十分讨厌驼峰原则,喜欢下划线分割的配置,并且期望所有表都这样,可以在初始化数据库连接的时候配置 define.underscored
,它会默成为你所有 define 的 默认 options。
const sequelize = new Sequelize('nodelover', 'root', '', {
host: 'localhost',
dialect: 'mysql',
define: {
underscored: true,
} ,
pool: {
max: 5,
min: 0,
idle: 10000
},
});
小栗子
接下来,同样通过一个例子来说明属于关系。就着之前的 User 与 Book 模型定义做一些修改。
接口是支持继承的,没必要把 UserAttributes 里面的属性再写一次,直接继承一下就好了。并且为加强一下记忆,再次阐述一下属性接口与实例接口的区别,attr
里面声明的是 create 创建的时候需要传递的属性,而 instance
接口里面的都是 sequelize 动态给类添加的方法与属性。
1. 新建 user.ts
import Sequelize from 'sequelize';
export interface UserAttributes {
email: string;
name: string;
}
export interface UserInstance extends Sequelize.Instance<UserAttributes>, UserAttributes {
id: number;
createdAt: Date;
updatedAt: Date;
say(): void;
}
export default function UserDefine(sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes): Sequelize.Model<UserInstance, UserAttributes> {
const S = dataTypes;
const User = sequelize.define<UserInstance, UserAttributes>
('User', {
email: S.STRING,
name: S.STRING
});
(User as any).prototype.say = function(this: UserInstance) {
console.log('name ' + this.name);
};
(User as any).associate = function(models){
}
return User;
}
在模型里面,可以在静态 associate 方法来定义模型之间的关系,这是官方所推荐的,假如使用官方提供的 migrate 工具会自动执行该方法。
在 prototype
上面加了一个 say 实例方法,所以在 instace
接口里面声明一下 say 方法。
2.book.ts
import Sequelize from 'sequelize';
import {UserInstance, UserAttributes} from './User'
export interface BookAttributes {
status: "inSale" | "noSale";
description: string;
title: string;
author: string;
}
export interface BookInstance extends Sequelize.Instance<BookAttributes>, BookAttributes{
id: number;
createdAt: Date;
updatedAt: Date;
setUser: Sequelize.BelongsToSetAssociationMixin<UserInstance, number>;
createUser: Sequelize.BelongsToCreateAssociationMixin<UserAttributes>;
User: Sequelize.BelongsToGetAssociationMixin<UserInstance>
}
export default function BookDefine(sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes): Sequelize.Model<BookInstance, BookAttributes> {
const S = dataTypes;
const Book = sequelize.define<BookInstance, BookAttributes>('Book', {
id: {
type: S.INTEGER,
autoIncrement: true,
primaryKey: true,
unique: true
},
description: S.TEXT,
status: {
type: S.ENUM,
values: ['inSale', 'noSale'],
validate: {
isIn: {
args: [['inSale', 'noSale']],
msg: "status field must be inSale or noSale"
}
}
},
title:{
type: S.STRING,
allowNull: false,
get(this: BookInstance) {
return this.getDataValue('author') + ' - ' + this.getDataValue('title');
}
},
author: {
type: S.STRING,
allowNull: false,
set(val: string){
this.setDataValue('author', val.toLowerCase());
}
}},{
comment: "图书表", // 表注释
indexes: [ // 表索引
{
fields: ['id']
}
],
classMethods: {
// associate: function(models){} 第一种
}
});
// 第二种
(Book as any).associate = function(this: typeof Book, models: any){
this.belongsTo(models.User);
}
return Book;
}
特别注意 第一种写法在 v4 版本将会被移除。具体可以查看官方的如何升级到 v4版本?
文档。
在 define 方法的 options 参数里是有 classMethods
配置项的,可以指定模型的静态方法,所以可以有俩种写法。这里用的是直接写,也就是第二种写法。在 associate
静态方法内, this
指的就是 Book
,而传递的 models 里面有所有 define 过的模型,这里定义的关系就是, Book 模型属于 User,所以会在 Book 实例上面添加 setUser
/ createUser
/ User
。
sequelize 动态添加了这些方法,但是 typescript 是不知道的,所以需要自己去声明,所以就有了。
setUser: Sequelize.BelongsToSetAssociationMixin<UserInstance, number>;
createUser: Sequelize.BelongsToCreateAssociationMixin<UserAttributes>;
User: Sequelize.BelongsToGetAssociationMixin<UserInstance>
这些 Mixin 是由定义库提供的一些接口。
3.index.ts
import Sequelize from 'sequelize';
const sequelize = new Sequelize('nodelover', 'root', '', {
host: 'localhost',
dialect: 'mysql',
pool: {
max: 5,
min: 0,
idle: 10000
},
});
import UserDefined , { UserAttributes, UserInstance } from './user';
import BookDefined, {BookAttributes, BookInstance} from './book';
async function main() {
try {
const User = sequelize.import<UserInstance, UserAttributes>('./user');
const Book = sequelize.import<BookInstance, BookAttributes>('./book');
Object.keys(sequelize.models).forEach(modelName => {
const model = (sequelize.models[modelName] as any)
if('associate' in model){
model.associate(sequelize.models)
}
})
await sequelize.sync();
let book = await Book.create({author:'alice', description: 'typescript hand book', status: 'inSale',title:'ts leaning'});
let user = await User.create({email:'belovedyogurt@gmail.com',name: 'yugo'})
book.setUser(user);
await book.save();
}catch(e){
// console.log(e)
if (e instanceof Sequelize.ValidationError) {
console.log(e.message);
}
}
process.exit(0)
sequelize.models
对象上面缓存了所有定义过的模型,通过以下几行代码,实现了自动调用 associate 方法,与同步数据库表。
Object.keys(sequelize.models).forEach(modelName => {
const model = (sequelize.models[modelName] as any)
if('associate' in model){
model.associate(sequelize.models)
}
})
await sequelize.sync();
运行以上代码,在数据库里面可以看到2张表都已经有了数据。
·>_<·! Nice Work!