教程 > sequelize 中文教程 > 阅读:213

sequelize 模型基础——迹忆客-ag捕鱼王app官网

在篇内容中,我们将学习 sequelize 中的模型以及如何使用它们。

概念

模型是 sequelize 的本质. 模型是代表数据库中表的抽象. 在 sequelize 中,它是一个 model 的扩展类.

该模型告诉 sequelize 有关它代表的实体的几件事,例如数据库中表的名称以及它具有的列(及其数据类型).

sequelize 中的模型有一个名称. 此名称不必与它在数据库中表示的表的名称相同. 通常,模型具有单数名称(例如,user),而表具有复数名称(例如, users),当然这是完全可配置的.


模型定义

在 sequelize 中可以用两种等效的方式定义模型:

  • 调用 sequelize.define(modelname, attributes, options)
  • 扩展 model 并调用 init(attributes, options)

定义模型后,可通过其模型名称在 sequelize.models 中使用该模型.

为了学习一个示例,我们将考虑创建一个代表用户的模型,该模型具有一个 firstname 和一个 lastname. 我们希望将模型称为 user,并将其表示的表在数据库中称为 users.

定义该模型的两种方法如下所示. 定义后,我们可以使用 sequelize.models.user 访问模型.

使用 sequelize.define:

const { sequelize, datatypes } = require('sequelize');
const sequelize = new sequelize('sqlite::memory:');
const user = sequelize.define('user', {
  // 在这里定义模型属性
  firstname: {
    type: datatypes.string,
    allownull: false
  },
  lastname: {
    type: datatypes.string
    // allownull 默认为 true
  }
}, {
  // 这是其他模型参数
});
// `sequelize.define` 会返回模型
console.log(user === sequelize.models.user); // true

扩展 model

const { sequelize, datatypes, model } = require('sequelize');
const sequelize = new sequelize('sqlite::memory:');
class user extends model {}
user.init({
  // 在这里定义模型属性
  firstname: {
    type: datatypes.string,
    allownull: false
  },
  lastname: {
    type: datatypes.string
    // allownull 默认为 true
  }
}, {
  // 这是其他模型参数
  sequelize, // 我们需要传递连接实例
  modelname: 'user' // 我们需要选择模型名称
});
// 定义的模型是类本身
console.log(user === sequelize.models.user); // true

在内部,sequelize.define 调用 model.init,因此两种方法本质上是等效的.

公共类字段的注意事项

添加与模型属性之一同名的公共类字段会出现问题. sequelize 为通过 model.init 定义的每个属性添加一个 getter 和一个 setter. 添加公共类字段将隐藏那些 getter 和 setter,从而阻止对模型的实际数据的访问.

// 无效的
class user extends model {
  id; // 此字段将影响 sequelize 的 getter 和 setter. 它应该被删除.
  otherpublicfield; // 这个字段不会影响任何东西. 没问题.
}
user.init({
  id: {
    type: datatypes.integer,
    autoincrement: true,
    primarykey: true
  }
}, { sequelize });
const user = new user({ id: 1 });
user.id; // undefined
// 有效的
class user extends model {
  otherpublicfield;
}
user.init({
  id: {
    type: datatypes.integer,
    autoincrement: true,
    primarykey: true
  }
}, { sequelize });
const user = new user({ id: 1 });
user.id; // 1

在 typescript 中, 我们可以使用 declare 关键字添加键入信息, 而无需添加实际的公共类字段:

// 有效
class user extends model {
  declare id: number; // 您可以使用 `declare` 关键字添加键入信息, 而无需添加实际的公共类字段.
}
user.init({
  id: {
    type: datatypes.integer,
    autoincrement: true,
    primarykey: true
  }
}, { sequelize });
const user = new user({ id: 1 });
user.id; // 1

表名推断

请注意,在以上两种方法中,都从未明确定义表名(users). 但是,给出了模型名称(user)。

默认情况下,当未提供表名时,sequelize 会自动将模型名复数并将其用作表名. 这种复数是通过称为 inflection 的库在后台完成的,因此可以正确计算不规则的复数(例如 person -> people)。

当然,此行为很容易配置。

强制表名称等于模型名称

你可以使用 freezetablename: true 参数停止 sequelize 执行自动复数化。 这样,sequelize 将推断表名称等于模型名称,而无需进行任何修改:

sequelize.define('user', {
  // ... (属性)
}, {
  freezetablename: true
});

上面的示例将创建一个名为 user 的模型,该模型指向一个也名为 user 的表。

也可以为 sequelize 实例全局定义此行为:

const sequelize = new sequelize('sqlite::memory:', {
  define: {
    freezetablename: true
  }
});

这样,所有表将使用与模型名称相同的名称。

直接提供表名

你也可以直接直接告诉 sequelize 表名称:

sequelize.define('user', {
  // ... (属性)
}, {
  tablename: 'employees'
});

模型同步

定义模型时,你要告诉 sequelize 有关数据库中表的一些信息. 但是,如果该表实际上不存在于数据库中怎么办? 如果存在,但具有不同的列,较少的列或任何其他差异,该怎么办?

这就是模型同步的来源.可以通过调用一个异步函数(返回一个promise)model.sync(options). 通过此调用,sequelize 将自动对数据库执行 sql 查询. 请注意,这仅更改数据库中的表,而不更改 javascript 端的模型.

  • user.sync() - 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)
  • user.sync({ force: true }) - 将创建表,如果表已经存在,则将其首先删除
  • user.sync({ alter: true }) - 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配.

示例:

await user.sync({ force: true });
console.log("用户模型表刚刚(重新)创建!");

一次同步所有模型

你可以使用 sequelize.sync() 自动同步所有模型。

示例:

await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");

删除表

删除与模型相关的表:

await user.drop();
console.log("用户表已删除!");

删除所有表:

await sequelize.drop();
console.log("所有表已删除!");

数据库安全检查

如上所示,sync和drop操作是破坏性的。 sequelize 使用 match 参数作为附加的安全检查,该检查将接受 regexp:

// 仅当数据库名称以 '_test' 结尾时,它才会运行.sync()
sequelize.sync({ force: true, match: /_test$/ });

生产环境同步

如上所示,sync({ force: true })sync({ alter: true }) 可能是破坏性操作。 因此,不建议将它们用于生产级软件中。 相反,应该在 sequelize cli 的帮助下使用高级概念 migrations(迁移) 进行同步。


时间戳

默认情况下,sequelize 使用数据类型 datatypes.date 自动向每个模型添加 createdat 和 updatedat 字段。 这些字段会自动进行管理 - 每当你使用sequelize 创建或更新内容时,这些字段都会被自动设置。 createdat 字段将包含代表创建时刻的时间戳,而 updatedat 字段将包含最新更新的时间戳。

注意 : 这是在 sequelize 级别完成的(即未使用 sql触发器 完成)。 这意味着直接 sql 查询(例如,通过任何其他方式在不使用 sequelize 的情况下执行的查询)将不会导致这些字段自动更新。

对于带有 timestamps: false 参数的模型,可以禁用此行为:

sequelize.define('user', {
  // ... (属性)
}, {
  timestamps: false
});

也可以只启用 createdat/updatedat 之一,并为这些列提供自定义名称:

class foo extends model {}
foo.init({ /* 属性 */ }, {
  sequelize,
  // 不要忘记启用时间戳!
  timestamps: true,
  // 不想要 createdat
  createdat: false,
  // 想要 updatedat 但是希望名称叫做 updatetimestamp
  updatedat: 'updatetimestamp'
});

列声明简写语法

如果关于列的唯一指定内容是其数据类型,则可以缩短语法:

// 例如:
sequelize.define('user', {
  name: {
    type: datatypes.string
  }
});
// 可以简写为:
sequelize.define('user', { name: datatypes.string });

默认值

默认情况下,sequelize 假定列的默认值为 null. 可以通过将特定的 defaultvalue 传递给列定义来更改此行为:

sequelize.define('user', {
  name: {
    type: datatypes.string,
    defaultvalue: "john doe"
  }
});

一些特殊的值,例如 datatypes.now,也能被接受:

sequelize.define('foo', {
  bar: {
    type: datatypes.datetime,
    defaultvalue: datatypes.now
    // 这样,当前日期/时间将用于填充此列(在插入时)
  }
});

数据类型

你在模型中定义的每一列都必须具有数据类型。 sequelize 提供很多内置数据类型。 要访问内置数据类型,必须导入 datatypes:

const { datatypes } = require("sequelize"); // 导入内置数据类型

字符串

datatypes.string             // varchar(255)
datatypes.string(1234)       // varchar(1234)
datatypes.string.binary      // varchar binary
datatypes.text               // text
datatypes.text('tiny')       // tinytext
datatypes.citext             // citext          仅 postgresql 和 sqlite.
datatypes.tsvector           // tsvector        仅 postgresql.

布尔

datatypes.boolean            // tinyint(1)

数字

datatypes.integer            // integer
datatypes.bigint             // bigint
datatypes.bigint(11)         // bigint(11)
datatypes.float              // float
datatypes.float(11)          // float(11)
datatypes.float(11, 10)      // float(11,10)
datatypes.real               // real            仅 postgresql.
datatypes.real(11)           // real(11)        仅 postgresql.
datatypes.real(11, 12)       // real(11,12)     仅 postgresql.
datatypes.double             // double
datatypes.double(11)         // double(11)
datatypes.double(11, 10)     // double(11,10)
datatypes.decimal            // decimal
datatypes.decimal(10, 2)     // decimal(10,2)

无符号和零填充整数 - 仅限于mysql/mariadb

在 mysql 和 mariadb 中,可以将数据类型integer, bigint, float 和 double 设置为无符号或零填充(或两者),如下所示:

datatypes.integer.unsigned
datatypes.integer.zerofill
datatypes.integer.unsigned.zerofill
// 你还可以指定大小,即integer(10)而不是简单的integer
// 同样适用于 bigint, float 和 double

日期

datatypes.date       // datetime 适用于 mysql / sqlite, 带时区的timestamp 适用于 postgres
datatypes.date(6)    // datetime(6) 适用于 mysql 5.6.4 . 支持6位精度的小数秒
datatypes.dateonly   // 不带时间的 date

uuid

对于 uuid,使用 datatypes.uuid。 对于 postgresql 和 sqlite,它会是 uuid 数据类型;对于 mysql,它则变成char(36)。 sequelize 可以自动为这些字段生成 uuid,只需使用 datatypes.uuidv1datatypes.uuidv4 作为默认值即可:

{
  type: datatypes.uuid,
  defaultvalue: datatypes.uuidv4 // 或 datatypes.uuidv1
}

其它

还有其他数据类型,请参见其它数据类型。


列参数

在定义列时,除了指定列的 type 以及上面提到的 allownulldefaultvalue 参数外,还有很多可用的参数。 下面是一些示例。

const { model, datatypes, deferrable } = require("sequelize");
class foo extends model {}
foo.init({
  // 实例化将自动将 flag 设置为 true (如果未设置)
  flag: { type: datatypes.boolean, allownull: false, defaultvalue: true },
  // 日期的默认值 => 当前时间
  mydate: { type: datatypes.date, defaultvalue: datatypes.now },
  // 将 allownull 设置为 false 将为该列添加 not null,
  // 这意味着如果该列为 null,则在执行查询时将从数据库引发错误.
  // 如果要在查询数据库之前检查值是否不为 null,请查看下面的验证部分.
  title: { type: datatypes.string, allownull: false },
  // 创建两个具有相同值的对象将引发错误.
  // unique 属性可以是布尔值或字符串.
  // 如果为多个列提供相同的字符串,则它们将形成一个复合唯一键.
  uniqueone: { type: datatypes.string,  unique: 'compositeindex' },
  uniquetwo: { type: datatypes.integer, unique: 'compositeindex' },
  // unique 属性是创建唯一约束的简写.
  someunique: { type: datatypes.string, unique: true },
  // 继续阅读有关主键的更多信息
  identifier: { type: datatypes.string, primarykey: true },
  // autoincrement 可用于创建 auto_incrementing 整数列
  incrementme: { type: datatypes.integer, autoincrement: true },
  // 你可以通过 'field' 属性指定自定义列名称:
  fieldwithunderscores: { type: datatypes.string, field: 'field_with_underscores' },
  // 可以创建外键:
  bar_id: {
    type: datatypes.integer,
    references: {
      // 这是对另一个模型的参考
      model: bar,
      // 这是引用模型的列名
      key: 'id',
      // 使用 postgresql,可以通过 deferrable 类型声明何时检查外键约束.
      deferrable: deferrable.initially_immediate
      // 参数:
      // - `deferrable.initially_immediate` - 立即检查外键约束
      // - `deferrable.initially_deferred` - 将所有外键约束检查推迟到事务结束
      // - `deferrable.not` - 完全不推迟检查(默认) - 这将不允许你动态更改事务中的规则
    }
  },
  // 注释只能添加到 mysql,mariadb,postgresql 和 mssql 的列中
  commentme: {
    type: datatypes.integer,
    comment: '这是带有注释的列'
  }
}, {
  sequelize,
  modelname: 'foo',
  // 在上面的属性中使用 `unique: true` 与在模型的参数中创建索引完全相同:
  indexes: [{ unique: true, fields: ['someunique'] }]
});

利用模型作为类

sequelize 模型是 es6 类。 你可以非常轻松地添加自定义实例或类级别的方法。

class user extends model {
  static classlevelmethod() {
    return 'foo';
  }
  instancelevelmethod() {
    return 'bar';
  }
  getfullname() {
    return [this.firstname, this.lastname].join(' ');
  }
}
user.init({
  firstname: sequelize.text,
  lastname: sequelize.text
}, { sequelize });
console.log(user.classlevelmethod()); // 'foo'
const user = user.build({ firstname: 'jane', lastname: 'doe' });
console.log(user.instancelevelmethod()); // 'bar'
console.log(user.getfullname()); // 'jane doe'

查看笔记

扫码一下
查看教程更方便
网站地图