跳到主要内容

sequelize 5.0中文文档 定义模型define model和验证 (二) - node.js语言最好用的orm

· 阅读需 13 分钟
一介布衣
全栈开发者 / 技术写作者
  • 文章目录
  • 前言

  • 定义模型

    • 语法
    • 给属性定义getter和setter函数
      • 定义为属性的一部分
      • 定义为模型的一部分
      • 用于getter和setter定义内部的Helper方法
  • 验证

  • 认证器和allowNull

  • 模型认证

  • 前言

在使用sequelize ORM框架时一定要先创建模型对象.
对象模型对应的就是数据库中表,字段及字段类型等的定义.
我们一般的做法是先在nodejs中将对象创建出来,然后调用Sequelize的同步方法,将数据库自动创建出来.
这样就避免了既要写代码建表,又要手工创建数据库中的表的操作.
只需要单独考虑代码中的对象类型等属性就好了.
如果数据库中已经建好了表,并且不能删除,这个时候就不能自动创建了,因为创建的时候会删除掉旧的数据

定义模型

名称类型说明
modelNameString模型名,在sequelize.models属性中会使用这个名称;
如果没有在options中指定表名,数据库中也会使用此属性做为表名
attributesObject一个对象,其每个属性对应表中的一个列,
每个列可以使用一个预定义的DataType、字符串或类型描述对象定义
attributes.columnStringObject 数据库中的列描述
attributes.column.typeStringDataType或字符串,表示列的数据类型
[attributes.column
.allowNull=true]Boolean设置为false时,
会给添加NOT NULL(非空)约束,数据保存时会进行非空验证
[attributes.column
.defaultValue=null]Any字面默认值, JavaScript函数, 或一个 SQL 函数 (查看 sequelize.fn)
[attributes.column
.unique=false]String设置为true时,会为列添加唯一约束
[attributes.column
.primaryKey=false]Boolean指定是否是主键
[attributes.column
.field=null]String设置在数据库中的字段名。
设置后会,Sequelize会将属性名映射到数据库中的不同名称
[attributes.column
.autoIncrement=false]Boolean是否自增
[attributes.column
.comment=null]String字段描述(自1.7+后,此描述不再添加到数据库中)
[attributes.column
.references=null]StringModel 引用对象
[attributes.column
.references.model]StringModel 如果列引用到另一个表,
可以通过这个属性设置模型或字符串。
[attributes.column
.references.key='id']String该列表示到表外键列的引用
[attributes.column.onUpdate]String当被引用的键更新时的操作,
可选值是:CASCADE, RESTRICT, SET DEFAULT, SET NULL 或 NO ACTION 之一
[attributes.column.onDelete]String当被引用的键删除时的操作,
可选值是:CASCADE, RESTRICT, SET DEFAULT, SET NULL 或 NO ACTION 之一
[attributes.column.get]Function为列自定义一个访问器。
使用this.getDataValue(String)时调用的值
[attributes.column.set]Function为列自定义一个设置器。
使用this.setDataValue(String, Value)时调用的值
[attributes.validate]Object模型每次保存时调用的验证对象。
可是validator.js中的验证函数(参见 DAOValidator)、或自定义的验证函数。
[options]Object提供给Sequelize 构造函数的一些默认值
[options.defaultScope={}]Object定义使用此模型的默认搜索范围。
作用范围与提供给 find / findAll 的选项形式相同
[options.scopes]Object更多范围,定义 defaultScope 的定义形式相同。
关于限制范围的定义请参考Model.scope
[options.omitNull]Boolean是否忽略空值,这意味着,
所有列的空值将不会被保存
[options.timestamps=true]Boolean为模型添加 createdAt 和
updatedAt 两个时间戳字段
[options.paranoid=false]Boolean使用逻辑删除。设置为true后,
调用 destroy 方法时将不会删队模型,而是设置一个 deletedAt 列。此设置需要 timestamps=true
[options.underscored=false]Boolean转换列名的驼峰命名规则为下划线命令规则
[options.underscoredAll=false]Boolean转换模型名的驼峰命名规则为表名的下划线命令规则
[options.freezeTableName=false]Boolean设置为true时,
sequelize不会改变表名,否则可能会按其规则有所调整
[options.name]Object允有singular 和 plural两个属性的对象,在模型与其它模型关联时使用
[options.name.singular=
inflection.singularize(modelName)]String
[options.name.plural=
inflection.pluralize(modelName)]String
[options.indexes]Array.要建立的索引
[options.indexes[].name]String索引名,默认为模型名 + '_' + 字段名
[options.indexes[].type]String索引类型,仅用于 mysql,
其值为:UNIQUE、 FULLTEXT 或 SPATIAL之一
[options.indexes[].method]String创建索引的方法(SQL中的USING 声明)。
BTREE 或 HASH 可以在 mysql 和 postgres中支持,postgres中支持,还支持 GIST 和 GIN
[options.indexes[].unique=false]Boolean设置索引是否唯一,
设置后会自动触发UNIQUE设置
[options.indexes[]
.concurrently=false]BooleanPostgreSQL 中在创建索引时不使用任务写锁定。
仅 Postgres 适用
[options.indexes[].fields]Array.<StringObject>

每个字段可以是一个字段名,sequelize 对象 (如 sequelize.fn),
或一个包含:attribute (字段名)、length (创建前缀字符数)、
order (列排序方向)、collate (较验的字段集合 (排序))|
|[options.createdAt] |String | 如果为字符串,
则使用提供的值代替 createdAt 列的默认名,设置为flase则不添加这个字段|
|[options.updatedAt] |String | 如果为字符串,
则使用提供的值代替 updatedAt 列的默认名,设置为flase则不添加这个字段|
|[options.deletedAt]| String | 如果为字符串,
则使用提供的值代替 deletedAt 列的默认名,设置为flase则不添加这个字段|
|[options.tableName]| String |模型所对应表的表名,
设置freezeTableName 为 true时,才会严格使用模型名|
|[options.getterMethods]| Object |提供给 getter 调用的方法,
与每列定义的访问器一样。如果为列定义了一个相同名称的 getter 方法,
那么会通过这个方法获取值;如果未定义的名称与列不匹配,
这将做为一个虚拟访问器;也用于设置多个值,但不能用在|
|[options.setterMethods] |Object |提供给 setter 调用的方法,
与每列定义的设置器一样。如果为列定义了一个相同名称的 setter 方法,
那么会通过这个方法设置值;如果未定义的名称与列不匹配,
这将做为一个虚拟访设置;也用于匹配多个值,但不用于逻辑删除|
|[options.instanceMethods]| Object| 提供给每个实例(DAO)的方法。
如果通过sequelize对方法进行了重写,可以通过"this.constructor.super_.prototype"
来调用原方法,如:this.constructor.super_.prototype.toJSON.apply(this, arguments)|
|[options.classMethods] |Object| 添加到Model的类方法,
如果通过sequelize对方法进行了重写,可以通过 this.constructor.prototype来调用原方法,
如:this.constructor.prototype.find.apply(this, arguments)|
|[options.schema='public'] |String ||
|[options.engine] |String ||
|[options.charset]| String ||
|[options.comment] |String ||
|[options.collate] |String ||
|[options.initialAutoIncrement] |String| MySQL中设置
AUTO_INCREMENT (自增)的初始值|
|[options.hooks] |Object |一个包含钩子函数的对象,
这些函数会在生生命周期内某些事件发生之前或之后被调用。
可添加的钩子函数有:beforeValidate, afterValidate, beforeBulkCreate,
beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy,
beforeUpdate, afterCreate, afterDestroy, afterUpdate, afterBulkCreate,
afterBulkDestory 和 afterBulkUpdate。每个属性可以是一个函数,或是一个包含一组函数的数组|
|[options.validate] |Object| 模型广泛验证对象。该验证会通过this。
如果验证函数中有参数,则会被认为是异步的,并通过一个包含可选错误的回调函数形式的的调|

语法

const Department = sequelize.define('department', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true //定义属性同时创建索引
}
}, {
//读写属性的自定义函数,下面有单独介绍
getterMethods: {
// 自定义函数, desc() 返回部门简要信息描述
desc() {
return `\${this.id} \${this.name} `;
},
},
setterMethods: {},

// classMethods 和 instanceMethods 在 版本4 被移除了
// 详见: http://docs.sequelizejs.com/manual/tutorial/upgrade-to-v4.html#config-options
// 定义 类 级别的函数,可以用 Account 调用
classMethods: {
getCMethod() {
return 'classMethods';
},
},
// 定义 实例 级别的函数,用 department 调用
instanceMethods: {
getIMethod() {
return 'instanceMethods';
},
},
// 也可以在 src/db/index 中定义全局的函数

// 设置为 false 之后,将不会自动加上 createdAt, updatedAt 这两个字段
timestamps: true,
// 假设我们需要 创建和更新 这两个字段,但不喜欢驼峰命名法
// 设置为 true 之后,自动增加的字段就会用下划线命名: created_at, updated_at
underscored: true,
// 也可以分别设置 createdAt, updatedAt 是否需要
// 假设我们喜欢 date 这个名字表示创建时间
createdAt: 'date',
// 不想要 updatedAt 这个字段
updatedAt: false,

// 设置为 true 之后,则不会真正的删除数据,而是设置 deletedAt
paranoid: true,
// 也可以重命名 deletedAt
deletedAt: 'deleteTime',

// 写表注释
comment: '账号表',
tableName: 'departments',//自定义表名,默认会生成一个 departments 的表
//统一索引设置
indexes:[
{
unique: true,
fields: ['name']//统一定义索引,和上面的属性定义索引一样的功能, 这里可以指定多个字段
},
]
});

Department.associate = (models) => {
Department.hasMany(models.Employee, {
constraints: false,
foreignKey: 'departmentId'
});
};

type 类型, 更多类型参考这里
autoIncrement 是否自增
primaryKey 是否主键
allowNull 是否可以为NULL
上面有2种定义索引的方法 可以直接属性中给 name 字段定义索引, 也可以在 indexes 数组中定义字段索引

给属性定义getter和setter函数

可以在模型上定义'对象属性'getter和setter函数,这些可以用于映射到数据库字段的“保护”属性,也可以用于定义“伪”属性.

Getters和Setters可以通过两种方式定义(您可以混合使用这两种方式)

  • 作为属性定义的一部分
  • 作为模型参数的一部分

注意 : 如果在两个地方定义了getter或setter,那么在相关属性定义中找到的函数始终是优先的

定义为属性的一部分

const Employee = sequelize.define('employee', {
name: {
type: Sequelize.STRING,
allowNull: false,
get() {
const title = this.getDataValue('title');// 'this' 允许你访问实例的属性
return this.getDataValue('name') + ' (' + title + ')';
},
},
title: {
type: Sequelize.STRING,
allowNull: false,
set(val) {
this.setDataValue('title', val.toUpperCase());
}
}
});

我们看下效果:

Employee
.create({ name: 'John Doe', title: 'senior engineer' })
.then(employee => {
console.log(employee.get('name')); // John Doe (SENIOR ENGINEER)
console.log(employee.get('title')); // SENIOR ENGINEER
})

定义为模型的一部分

属性的getter 和 setter 为对象上定义的伪属性 - 这些属性实际上不是数据库模式的一部分

const People = sequelize.define('people', {
firstname: Sequelize.STRING,
lastname: Sequelize.STRING
}, {
getterMethods: {
fullName() {
return this.firstname + ' ' + this.lastname
}
},

setterMethods: {
fullName(value) {
const names = value.split(' ');

this.setDataValue('firstname', names.slice(0, -1).join(' '));
this.setDataValue('lastname', names.slice(-1).join(' '));
},
}
});

用于getter和setter定义内部的Helper方法

检索底层属性值 - 总是使用 this.getDataValue()

/* 一个用于 'title' 属性的 getter */
get() {
return this.getDataValue('title')
}

设置基础属性值 - 总是使用 this.setDataValue()

/* 一个用于 'title' 属性的 setter */
set(title) {
this.setDataValue('title', title.toString().toLowerCase());
}

注意 : 坚持使用 setDataValue() 和 getDataValue() 函数(而不是直接访问底层的“数据值”属性)是非常重要的 - 这样做可以保护您的定制getter和setter不受底层模型实现的变化

验证

模型验证,允许您为模型的每个属性指定格式/内容/继承验证。
验证会自动运行在 create , update 和 save 上。 你也可以调用 validate() 手动验证一个实例
验证由 validator.js 实现

const ValidateMe = sequelize.define('foo', {
foo: {
type: Sequelize.STRING,
validate: {
is: ["^[a-z]+$",'i'], // 只允许字母
is: /^[a-z]+$/i, // 与上一个示例相同,使用了真正的正则表达式
not: ["[a-z]",'i'], // 不允许字母
isEmail: true, // 检查邮件格式 (foo@bar.com)
isUrl: true, // 检查连接格式 (http://foo.com)
isIP: true, // 检查 IPv4 (129.89.23.1) 或 IPv6 格式
isIPv4: true, // 检查 IPv4 (129.89.23.1) 格式
isIPv6: true, // 检查 IPv6 格式
isAlpha: true, // 只允许字母
isAlphanumeric: true, // 只允许使用字母数字
isNumeric: true, // 只允许数字
isInt: true, // 检查是否为有效整数
isFloat: true, // 检查是否为有效浮点数
isDecimal: true, // 检查是否为任意数字
isLowercase: true, // 检查是否为小写
isUppercase: true, // 检查是否为大写
notNull: true, // 不允许为空
isNull: true, // 只允许为空
notEmpty: true, // 不允许空字符串
equals: 'specific value', // 只允许一个特定值
contains: 'foo', // 检查是否包含特定的子字符串
notIn: [['foo', 'bar']], // 检查是否值不是其中之一
isIn: [['foo', 'bar']], // 检查是否值是其中之一
notContains: 'bar', // 不允许包含特定的子字符串
len: [2,10], // 只允许长度在2到10之间的值
isUUID: 4, // 只允许uuids
isDate: true, // 只允许日期字符串
isAfter: "2011-11-05", // 只允许在特定日期之后的日期字符串
isBefore: "2011-11-05", // 只允许在特定日期之前的日期字符串
max: 23, // 只允许值 &lt;= 23
min: 23, // 只允许值 &gt;= 23
isCreditCard: true, // 检查有效的信用卡号码

// 也可以自定义验证:
isEven(value) {
if (parseInt(value) % 2 != 0) {
throw new Error('Only even values are allowed!')
// 我们也在模型的上下文中,所以如果它存在的话,
// this.otherField会得到otherField的值。
}
}
}
}
});

请注意,如果需要将多个参数传递给内置的验证函数,则要传递的参数必须位于数组中. 但是,如果要传递单个数组参数,例如isIn的可接受字符串数组,则将被解释为多个字符串参数,而不是一个数组参数. 要解决这个问题,传递一个单一长度的参数数组,比如[['one','two']]

要使用自定义错误消息,而不是由validator.js提供,使用对象,而不是明文或参数数组,例如不需要任何参数可以给出一个自定义消息验证程序

isInt: {
msg: "Must be an integer number of pennies"
}

或者需要传递多个参数

isIn: {
args: [['en', 'zh']],
msg: "Must be English or Chinese"
}

当使用自定义的验证功能,该错误信息将是任何消息抛出Error对象持有。
查看 validator.js project 了解内置验证方法的更多细节
提示:您还可以定义日志部分自定义函数。只是传递一个函数。第一个参数将被记录。

认证器和allowNull

如果模型的列被设置为 allowNull:true 和 值被设置为 null的话,那认证器不会运行.
举个栗子, 一个字符串的列长度设置为 最短为5, 但它可以存储 null

模型认证

var Pub = Sequelize.define('pub', {
name: { type: Sequelize.STRING },
address: { type: Sequelize.STRING },
latitude: {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: null,
validate: { min: -90, max: 90 }
},
longitude: {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: null,
validate: { min: -180, max: 180 }
},
}, {

//在这里对模型进行验证,只有 latitude 和 longtitude 同时被给予或都为空时成立
validate: {
bothCoordsOrNone: function() {
if ((this.latitude === null) !== (this.longitude === null)) {
throw new Error('Require either both latitude and longitude or neither')
}
}
}
})