模型字段类型

在上一节中,字段的配置是这样的。S则是引入的Sequelize,为了编写方便,重命名了一下。

  1. {
  2. email: S.STRING,
  3. name: S.STRING
  4. }

在 define 函数的声明里面,需要传入的字段配置项其实是 DefineAttributes 类型。

  1. [name: string]: string | DataTypeAbstract | DefineAttributeColumnOptions;

定义如上,这表示这对象里面的 key 必须是一个 string,而 value 则是 string 、DataTypeAbstract 、DefineAttributeColumnOptions 三者其一即可。

1.DataTypeAbstract

DataTypeAbstract 只是一个父接口,而 DataTypeString 、DataTypeBigInt 、DataTypeFloat 则都是继承了这个父接口,也就是说我们传入 DataTypeString 类型也是可以的,因为在面向对象的继承中,子类是可以赋值给父类的。

而我们的 DataTypes 的定义是这样的,也就是我们所有支持的类型,但是数据库之间是存在差异的,像 MYSQL 就不支持 JSON 类、Array 类等。

  1. interface DataTypes {
  2. ABSTRACT: DataTypeAbstract;
  3. STRING: DataTypeString;
  4. CHAR: DataTypeChar;
  5. TEXT: DataTypeText;
  6. NUMBER: DataTypeNumber;
  7. INTEGER: DataTypeInteger;
  8. BIGINT: DataTypeBigInt;
  9. FLOAT: DataTypeFloat;
  10. TIME: DataTypeTime;
  11. DATE: DataTypeDate;
  12. DATEONLY: DataTypeDateOnly;
  13. BOOLEAN: DataTypeBoolean;
  14. NOW: DataTypeNow;
  15. BLOB: DataTypeBlob;
  16. DECIMAL: DataTypeDecimal;
  17. NUMERIC: DataTypeDecimal;
  18. UUID: DataTypeUUID;
  19. UUIDV1: DataTypeUUIDv1;
  20. UUIDV4: DataTypeUUIDv4;
  21. HSTORE: DataTypeHStore;
  22. JSON: DataTypeJSONType;
  23. JSONB: DataTypeJSONB;
  24. VIRTUAL: DataTypeVirtual;
  25. ARRAY: DataTypeArray;
  26. NONE: DataTypeVirtual;
  27. ENUM: DataTypeEnum;
  28. RANGE: DataTypeRange;
  29. REAL: DataTypeReal;
  30. DOUBLE: DataTypeDouble;
  31. "DOUBLE PRECISION": DataTypeDouble;
  32. GEOMETRY: DataTypeGeometry;
  33. }

并且 interface SequelizeStatic extends SequelizeStaticAndInstance, DataTypes,也就是说 SequelizeStatic 是继承了 DataTypes 的,所以我么可以通过 Sequelize.INTEGER来访问 DataTypes 的一些变量。

2.DefineAttributeColumnOptions

首先继承了 ColumnOptions 接口,都是一些可选属性,了解数据库知识的同学,对大部分配置项应该都能猜到是干什么用的。

  1. interface ColumnOptions {
  2. allowNull?: boolean;
  3. field?: string;
  4. defaultValue?: any;
  5. }
  1. interface DefineAttributeColumnOptions extends ColumnOptions {
  2. type: string | DataTypeAbstract;
  3. unique?: boolean | string | { name: string, msg: string };
  4. primaryKey?: boolean;
  5. autoIncrement?: boolean;
  6. comment?: string;
  7. references?: DefineAttributeColumnReferencesOptions;
  8. onUpdate?: string;
  9. onDelete?: string;
  10. get?: () => any;
  11. set?: (val: any) => void;
  12. validate?: DefineValidateOptions;
  13. values?: string[];
  14. }

同时有这个validate这个选项,其实是支持验证的。也就是用了一个validator的库。它所支持的配置项如下。

  1. interface DefineValidateOptions {
  2. is?: string | Array<string | RegExp> | RegExp | { msg: string, args: string | Array<string | RegExp> | RegExp };
  3. not?: string | Array<string | RegExp> | RegExp | { msg: string, args: string | Array<string | RegExp> | RegExp };
  4. isEmail?: boolean | { msg: string };
  5. isUrl?: boolean | { msg: string };
  6. isIP?: boolean | { msg: string };
  7. isIPv4?: boolean | { msg: string };
  8. isIPv6?: boolean | { msg: string };
  9. isAlpha?: boolean | { msg: string };
  10. isAlphanumeric?: boolean | { msg: string };
  11. isNumeric?: boolean | { msg: string };
  12. isInt?: boolean | { msg: string };
  13. isFloat?: boolean | { msg: string };
  14. isDecimal?: boolean | { msg: string };
  15. isLowercase?: boolean | { msg: string };
  16. isUppercase?: boolean | { msg: string };
  17. notNull?: boolean | { msg: string };
  18. isNull?: boolean | { msg: string };
  19. notEmpty?: boolean | { msg: string };
  20. equals?: string | { msg: string };
  21. contains?: string | { msg: string };
  22. notIn?: string[][] | { msg: string, args: string[][] };
  23. isIn?: string[][] | { msg: string, args: string[][] };
  24. notContains?: string[] | string | { msg: string, args: string[] | string };
  25. len?: [number, number] | { msg: string, args: [number, number] };
  26. isUUID?: number | { msg: string, args: number };
  27. isDate?: boolean | { msg: string, args: boolean };
  28. isAfter?: string | { msg: string, args: string };
  29. isBefore?: string | { msg: string, args: string };
  30. max?: number | { msg: string, args: number };
  31. min?: number | { msg: string, args: number };
  32. isArray?: boolean | { msg: string, args: boolean };
  33. isCreditCard?: boolean | { msg: string, args: boolean };
  34. [name: string]: any;
  35. }

小荔枝

  1. interface BookAttributes {
  2. // status: "inSale" | "noSale";
  3. status: any;
  4. description: string;
  5. title: string;
  6. author: string;
  7. }
  8. interface BookInstance extends Sequelize.Instance<BookAttributes> {
  9. id: number;
  10. createdAt: Date;
  11. updatedAt: Date;
  12. // status: "inSale" | "noSale";
  13. status: any;
  14. description: string;
  15. title: string;
  16. author: string;
  17. }
  18. const Book = sequelize.define<BookInstance, BookAttributes>('Book', {
  19. id: {
  20. type: S.INTEGER,
  21. autoIncrement: true,
  22. primaryKey: true,
  23. unique: true
  24. },
  25. description: S.TEXT,
  26. status: {
  27. type: S.ENUM,
  28. values: ['inSale', 'noSale'],
  29. validate: {
  30. isIn: {
  31. args: [['inSale', 'noSale']],
  32. msg: "status field must be inSale or noSale"
  33. }
  34. }
  35. },
  36. title:{
  37. type: S.STRING,
  38. allowNull: false,
  39. get(this: BookInstance) {
  40. return this.getDataValue('author') + ' - ' + this.getDataValue('title');
  41. }
  42. },
  43. author: {
  44. type: S.STRING,
  45. allowNull: false,
  46. set(val: string){
  47. this.setDataValue('author', val.toLowerCase());
  48. }
  49. },
  50. userId:{ // User 表的外键
  51. type: S.INTEGER,
  52. references: {
  53. model: User
  54. }
  55. }
  56. },{
  57. comment: "图书表", // 表注释
  58. indexes: [ // 表索引
  59. {
  60. fields: ['id']
  61. }
  62. ]
  63. })

// status: "inSale" | "noSale"; 注释掉这个的原因是为了避免ts 编译时报错,我们先给它一个 any 类型,来验证一下 validate 方法是否真正的起了验证作用。

main 方法

  1. async function main() {
  2. try {
  3. // await Book.sync();
  4. // await User.sync();
  5. Book.beforeValidate(() => {
  6. console.log("验证之前");
  7. });
  8. const book = Book.build({ status: "onSale", author: "yugo", description: "lean ts", title: "typescript" });
  9. await book.validate()
  10. }catch(e){
  11. // console.log(e)
  12. if (e instanceof Sequelize.ValidationError) {
  13. console.log(e.message);
  14. }
  15. }
  16. process.exit(0)
  17. }

运行可得到如下结果,说明验证错误是被 catch 捕获到了的,而且也打印了出来。

  1. # Yugo @ Tractor in ~/Desktop/nl-sequelize/code/chapter2 on git:master x [13:19:00]
  2. $ ts-node index.ts
  3. 验证之前
  4. Validation error: status field must be inSale or noSale

当把book.validate改成book.save同样也会自动调用验证方法。接下来,把前面的接口改回来,再来试一试正确的。

  1. const book = Book.build({ status: "inSale", author: "yugo", description: "lean ts", title: "typescript" });
  2. await book.save()
  1. # Yugo @ Tractor in ~/Desktop/nl-sequelize/code/chapter2 on git:master x [13:27:55]
  2. $ ts-node index.ts
  3. 验证之前
  4. Executing (default): INSERT INTO `Books` (`id`,`description`,`status`,`title`,`author`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'lean ts','inSale','typescript','yugo','2017-06-19 05:29:35','2017-06-19 05:29:35');

小提示: 有的时候可能提示并没有告诉你可以调用哪些方法,大多数是因为代码编辑器的问题,直接去查看d.ts,自己找一下即可。