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

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

image.png


文章目录



前言

在使用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,                  // 只允许值 <= 23
      min: 23,                  // 只允许值 >= 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')
      }
    }
  }
})

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

回到顶部