Sequelize(2) 定义表格

定义

定义模型与表之间的映射,使用 define 方法.

Sequelize 会自动增加 createdAtupdated 属性,这样能知道一个实例的创建时间和最终修改时间.如果不想自动生成,可以到configuration去看如何实现.

var Project = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
})

var Task = sequelize.define('task', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT,
  deadline: Sequelize.DATE
})

还可以为每一个列增加一些可选属性

var Foo = sequelize.define('foo', {
 // 自动设置默认值为 true
 flag: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},

 //  日期默认值 => 当前时间
 myDate: { type: Sequelize.DATE, defaultValue: Sequelize.NOW },

 // 设置列的 allowNull为 false 将会为该列增加 非空 属性
 // 在查询数据库之前想检车一个值是否为 空 ,看 validation 节
 title: { type: Sequelize.STRING, allowNull: false},
 
 // 创建两个拥有相同属性的值会抛出一个错误
 // The unique property can be either a boolean, or a string.
 // If you provide the same string for multiple columns, they will form a
 // composite unique key.
 someUnique: {type: Sequelize.STRING, unique: true},
 uniqueOne: { type: Sequelize.STRING,  unique: 'compositeIndex'},
 uniqueTwo: { type: Sequelize.INTEGER, unique: 'compositeIndex'}
 
 // unique 属性同时也是创建一个 unique 索引 的简写
 someUnique: {type: Sequelize.STRING, unique: true}
 
 // 跟下面的两句定义语句等价
 {someUnique: {type: Sequelize.STRING}},
 {indexes: [{unique: true, fields: ['someUnique']}]}

 // 主码
 identifier: { type: Sequelize.STRING, primaryKey: true},
 
 // 自增
 incrementMe: { type: Sequelize.INTEGER, autoIncrement: true },

 // 注释,只有 MySQL 和 PG 可以使用
 // Comments can be specified for each field for MySQL and PG
 hasComment: { type: Sequelize.INTEGER, comment: "I'm a comment!" },
 
 // 在模型中的名字是小驼峰,在表中的列名可以用 field 属性来指定
 fieldWithUnderscores: { type: Sequelize.STRING, field: "field_with_underscores" },

 // 创建外码
 bar_id: {
   type: Sequelize.INTEGER,

   references: {
     // This is a reference to another model
     model: Bar,
     //被引用模型的  列名  (是列名,即 field 名)
     key: 'id',
     // 检查外码约束,只支持 PostgreSQL .
     deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE
   }
 }
})

数据类型

更详细的可以上 DataTypes 看一看

Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT
Sequelize.TEXT('tiny')                // TINYTEXT

Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)

Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 12)               // FLOAT(11,12)

Sequelize.REAL                        // REAL        PostgreSQL only.
Sequelize.REAL(11)                    // REAL(11)    PostgreSQL only.
Sequelize.REAL(11, 12)                // REAL(11,12) PostgreSQL only.

Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 12)              // DOUBLE(11,12)

Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)

Sequelize.DATE                        // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATE(6)                     // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision 
Sequelize.DATEONLY                    // DATE without time.
Sequelize.BOOLEAN                     // TINYINT(1)

Sequelize.ENUM('value 1', 'value 2')  // An ENUM with allowed values 'value 1' and 'value 2'
Sequelize.ARRAY(Sequelize.TEXT)       // Defines an array. PostgreSQL only.

Sequelize.JSON                        // JSON column. PostgreSQL only.
Sequelize.JSONB                       // JSONB column. PostgreSQL only.

Sequelize.BLOB                        // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny')                // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)

Sequelize.UUID                        // UUID datatype for PostgreSQL and SQLite, CHAR(36) BINARY for MySQL (use defaultValue: Sequelize.UUIDV1 or Sequelize.UUIDV4 to make sequelize generate the ids automatically)


Sequelize.GEOMETRY                    // Spatial column.  PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT')           // Spatial column with geomerty type.  PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT', 4326)     // Spatial column with geomerty type and SRID.  PostgreSQL (with PostGIS) or MySQL only.

integer, bigint, float 和 double 同样支持 unsigned 和 zerofill 约束

Sequelize.INTEGER.UNSIGNED              // INTEGER UNSIGNED
Sequelize.INTEGER(11).UNSIGNED          // INTEGER(11) UNSIGNED
Sequelize.INTEGER(11).ZEROFILL          // INTEGER(11) ZEROFILL
Sequelize.INTEGER(11).ZEROFILL.UNSIGNED // INTEGER(11) UNSIGNED ZEROFILL
Sequelize.INTEGER(11).UNSIGNED.ZEROFILL // INTEGER(11) UNSIGNED ZEROFILL

对象标记的用法

// for enums:
sequelize.define('model', {
  states: {
    type:   Sequelize.ENUM,
    values: ['active', 'pending', 'deleted']
  }
})

Getters & Setters 方法

在模型中定义'对象-属性'的 getter 和 setter 方法是可能的,可以被用来保护与数据库列相映射的属性,还可以定义一些属性
Getters 和 Setters 能以下面两种方式定义

  • 作为单个属性定义的一部分
  • 作为模型可选的一部分

N.B: If a getter or setter is defined in both places then the function found in the relevant property definition will always take precedence.

定义为属性的一部分

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

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

定义为模型的一部分

下面是一个在模型内定义 getter 和 setter 方法的例子. 在这个例子里,fullname 的 getter 方法是在模型内定义假属性的一个例子
,因为 fullname 属性不存在于数据库模式中. 实际上,假属性可以通过以下两种方式来定义

  • 通过模型的 getter 方法
  • 或者通过使用带有 VIRTUAL 数据类型的列, VIRTUAL 数据类型可以有 validation , 然而 virtual 数据类型的 getter 方法不行

注意在 fullName 的 getter 方法中引用的 this.firstNamethis.lastName 会触发各自的 getter 方法

var Foo = sequelize.define('foo', {
  firstname: Sequelize.STRING,
  lastname: Sequelize.STRING
}, {
  getterMethods   : {
    fullName       : function()  { return this.firstname + ' ' + this.lastname }
  },
  
  setterMethods   : {
    fullName       : function(value) {
        var names = value.split(' ');
        this.setDataValue('firstname', names.slice(0, -1).join(' '));
        this.setDataValue('lastname', names.slice(-1).join(' '));
    },
  }
});

在 getter 和 setter 方法中使用的 Helper 方法

  • 获取一个基础属性值 , 经常用 this.getDataValue()
  • 设置一个基础属性值 , 经常用 this.setDataValue()
  • 坚持使用 getter 和 setter 方法 可以保护内部数据
/* 'title' 属性的 getter 方法*/
function(){
    return this.getDataValue('title');
}

/* 'title' 属性的 setter 方法*/
function(){
    return this.setDataValue('title',title.toString().toLowerCase());
}

认证 (Validation)

模型认证, 可以规定模型中每个属性的格式/内容 的认证. 其实现在

当进行 create,updatesave操作时,认证会自动运行.也可以在实例中手动认证

var ValidateMe = sequelize.define('foo', {
  foo: {
    type: Sequelize.STRING,
    validate: {
      is: ["^[a-z]+$",'i'],     // will only allow letters
      is: /^[a-z]+$/i,          // same as the previous example using real RegExp
      not: ["[a-z]",'i'],       // will not allow letters
      isEmail: true,            // checks for email format (foo@bar.com)
      isUrl: true,              // checks for url format (http://foo.com)
      isIP: true,               // checks for IPv4 (129.89.23.1) or IPv6 format
      isIPv4: true,             // checks for IPv4 (129.89.23.1)
      isIPv6: true,             // checks for IPv6 format
      isAlpha: true,            // will only allow letters
      isAlphanumeric: true,     // will only allow alphanumeric characters, so "_abc" will fail
      isNumeric: true,          // will only allow numbers
      isInt: true,              // checks for valid integers
      isFloat: true,            // checks for valid floating point numbers
      isDecimal: true,          // checks for any numbers
      isLowercase: true,        // checks for lowercase
      isUppercase: true,        // checks for uppercase
      notNull: true,            // won't allow null
      isNull: true,             // only allows null
      notEmpty: true,           // don't allow empty strings
      equals: 'specific value', // only allow a specific value
      contains: 'foo',          // force specific substrings
      notIn: [['foo', 'bar']],  // check the value is not one of these
      isIn: [['foo', 'bar']],   // check the value is one of these
      notContains: 'bar',       // don't allow specific substrings
      len: [2,10],              // only allow values with length between 2 and 10
      isUUID: 4,                // only allow uuids
      isDate: true,             // only allow date strings
      isAfter: "2011-11-05",    // only allow date strings after a specific date
      isBefore: "2011-11-05",   // only allow date strings before a specific date
      max: 23,                  // only allow values
      min: 23,                  // only allow values >= 23
      isArray: true,            // only allow arrays
      isCreditCard: true,       // check for valid credit card numbers

      // custom validations are also possible:
      isEven: function(value) {
        if(parseInt(value) % 2 != 0) {
          throw new Error('Only even values are allowed!')
        // we also are in the model's context here, so this.otherField
        // would get the value of otherField if it existed
        }
      }
    }
  }
});

注意当多个参数需要被传递到内嵌的认证函数时,多个参数应该被放在一个数组中.

isIn: [['en', 'zh']],   // check the value is one of these

不用 validator.js 提供的错误信息,自己定制错误信息

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

可以看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')
      }
    }
  }
})

配置

var Bar = sequelize.define('bar', { /* bla */ }, {

  // 不增加 TIMESTAMP 属性  (updatedAt, createdAt)
  timestamps: false,
  
  //不删除数据库中原有项, 增加新属性 deletedAt 并设置为 当前日期,
  //只有 TIMESTAMP 属性是允许的时候有效
  paranoid: true,
  
  // 不要使用驼峰式语法,用下划线代替
  // so updatedAt will be updated_at
  underscored: true,
  
  // 不允许调整表名 ; 
  // 默认地, sequelize 会自动转换所有传递的模型名字(define 的第一个参数)
  // 为复数
  // 如果不想这样,设置为 true
  freezeTableName: true,

  // 定义表名
  tableName: 'my_very_custom_table_name'
})

修改引擎

var Person = sequelize.define('person', { /* attributes */ }, {
  engine: 'MYISAM'
})

// or globally
var sequelize = new Sequelize(db, user, pw, {
  define: { engine: 'MYISAM' }
})

做注释

var Person = sequelize.define('person', { /* attributes */ }, {
  comment: "I'm a table comment!"
})

导入

可以把对表的定义放在单一文件中,返回对象恰好跟在 import 方法中定义的一样


  // in your server file - e.g. app.js
var Project = sequelize.import(__dirname + "/path/to/models/project")

  // 定义在 project.js 文件中已经定义完成了
  // DataTypes  与上面解释的部分很像
module.exports = function(sequelize, DataTypes) {
  return sequelize.define("project", {
    name: DataTypes.STRING,
    description: DataTypes.TEXT
  })
}

sequelize.import('project', function(sequelize, DataTypes) {
  return sequelize.define("project", {
    name: DataTypes.STRING,
    description: DataTypes.TEXT
  })
})

数据库同步

开始一个新项目的时候,我们没有数据库架构,于是我们不需要 Sequelize.

只需要说明我们的模型结构, 让库做剩下的部分,现在只支持 表的创建与删除

// 只同步还没在数据库中的模型
// Sync all models that aren't already in the database
sequelize.sync()

// 强制同步所有数据库的模型
sequelize.sync({force: true})

// 删除所有表
sequelize.drop()

// 开始执行
sequelize.[sync|drop]().then(function() {
  // woot woot
}).catch(function(error) {
  // whooops
})

因为.sync({force: true}) 是毁灭性的操作, 可以使用 match 选项去增加更多的安全检查(正则)

// 删除以 '_test' 结尾的词
sequelize.sync({ force: true, match: /_test$/ });

模型的扩展

Sequelize 允许我们去给 模型和相应的实例 传递定制方法

var sequelize = new Sequelize('database', 'username', 'password', {
  // 其他可选的初始化可以放在这里
  define: {
    classMethods: {
      method1: function() {},
      method2: function() {}
    },
    instanceMethods: {
      method3: function() {}
    }
  }
})

// Example:
var Foo = sequelize.define('foo', { /* attributes */});
Foo.method1()
Foo.method2()
Foo.build().method3()

索引

Sequelize 支持在模型定义中增加索引, 这些索引在 Model.sync()sequelize.sync()时被创建.

sequelize.define('user', {}, {
  indexes: [
    // Create a unique index on email
    {
      unique: true,
      fields: ['email']
    },

    // Creates a gin index on data with the jsonb_path_ops operator
    {
      fields: ['data'],
      using: 'gin',
      operator: 'jsonb_path_ops'
    },

    // By default index name will be [table]_[fields]
    // Creates a multi column partial index
    {
      name: 'public_by_author',
      fields: ['author', 'status'],
      where: {
        status: 'public'
      }
    },

    // A BTREE index with a ordered field
    {
      name: 'title_index',
      method: 'BTREE',
      fields: ['author', {attribute: 'title', collate: 'en_US', order: 'DESC', length: 5}]
    }
  ]
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容