当前位置: 首页 > 文档资料 > Sequelize ORM 实践 >

2.2 模型字段类型

优质
小牛编辑
133浏览
2023-12-01

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

{
    email: S.STRING,
    name: S.STRING
}

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

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

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

1.DataTypeAbstract

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

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

interface DataTypes {
   ABSTRACT: DataTypeAbstract;
   STRING: DataTypeString;
   CHAR: DataTypeChar;
   TEXT: DataTypeText;
   NUMBER: DataTypeNumber;
   INTEGER: DataTypeInteger;
   BIGINT: DataTypeBigInt;
   FLOAT: DataTypeFloat;
   TIME: DataTypeTime;
   DATE: DataTypeDate;
   DATEONLY: DataTypeDateOnly;
   BOOLEAN: DataTypeBoolean;
   NOW: DataTypeNow;
   BLOB: DataTypeBlob;
   DECIMAL: DataTypeDecimal;
   NUMERIC: DataTypeDecimal;
   UUID: DataTypeUUID;
   UUIDV1: DataTypeUUIDv1;
   UUIDV4: DataTypeUUIDv4;
   HSTORE: DataTypeHStore;
   JSON: DataTypeJSONType;
   JSONB: DataTypeJSONB;
   VIRTUAL: DataTypeVirtual;
   ARRAY: DataTypeArray;
   NONE: DataTypeVirtual;
   ENUM: DataTypeEnum;
   RANGE: DataTypeRange;
   REAL: DataTypeReal;
   DOUBLE: DataTypeDouble;
   "DOUBLE PRECISION": DataTypeDouble;
   GEOMETRY: DataTypeGeometry;
}

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

2.DefineAttributeColumnOptions

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

interface ColumnOptions {
   allowNull?: boolean;
   field?: string;
   defaultValue?: any;
}
interface DefineAttributeColumnOptions extends ColumnOptions {
   type: string | DataTypeAbstract;
   unique?: boolean | string | { name: string, msg: string };
   primaryKey?: boolean;
   autoIncrement?: boolean;
   comment?: string;
   references?: DefineAttributeColumnReferencesOptions;
   onUpdate?: string;
   onDelete?: string;
   get?: () => any;
   set?: (val: any) => void;
   validate?: DefineValidateOptions;
   values?: string[];
}

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

interface DefineValidateOptions {
   is?: string | Array<string | RegExp> | RegExp | { msg: string, args: string | Array<string | RegExp> | RegExp };
   not?: string | Array<string | RegExp> | RegExp | { msg: string, args: string | Array<string | RegExp> | RegExp };
   isEmail?: boolean | { msg: string };
   isUrl?: boolean | { msg: string };
   isIP?: boolean | { msg: string };
   isIPv4?: boolean | { msg: string };
   isIPv6?: boolean | { msg: string };
   isAlpha?: boolean | { msg: string };
   isAlphanumeric?: boolean | { msg: string };
   isNumeric?: boolean | { msg: string };
   isInt?: boolean | { msg: string };
   isFloat?: boolean | { msg: string };
   isDecimal?: boolean | { msg: string };
   isLowercase?: boolean | { msg: string };
   isUppercase?: boolean | { msg: string };
   notNull?: boolean | { msg: string };
   isNull?: boolean | { msg: string };
   notEmpty?: boolean | { msg: string };
   equals?: string | { msg: string };
   contains?: string | { msg: string };
   notIn?: string[][] | { msg: string, args: string[][] };
   isIn?: string[][] | { msg: string, args: string[][] };
   notContains?: string[] | string | { msg: string, args: string[] | string };
   len?: [number, number] | { msg: string, args: [number, number] };
   isUUID?: number | { msg: string, args: number };
   isDate?: boolean | { msg: string, args: boolean };
   isAfter?: string | { msg: string, args: string };
   isBefore?: string | { msg: string, args: string };
   max?: number | { msg: string, args: number };
   min?: number | { msg: string, args: number };
   isArray?: boolean | { msg: string, args: boolean };
   isCreditCard?: boolean | { msg: string, args: boolean };  
   [name: string]: any;
}

小荔枝

interface BookAttributes {
  // status: "inSale" | "noSale";
  status: any;
  description: string;
  title: string;
  author: string;
}

interface BookInstance extends Sequelize.Instance<BookAttributes> {
  id: number;
  createdAt: Date;
  updatedAt: Date;

  // status: "inSale" | "noSale";
  status: any;
  description: string;
  title: string;
  author: string;
}

const Book = sequelize.define<BookInstance, BookAttributes>('Book', {
  id: {
    type: S.INTEGER,
    autoIncrement: true,
    primaryKey: true,
    unique: true
  },
  description: S.TEXT,
  status: {
    type: S.ENUM,
    values: ['inSale', 'noSale'],
    validate: {
      isIn: {
        args: [['inSale', 'noSale']],
        msg: "status field must be inSale or noSale"
      }
    }
  },
  title:{
    type: S.STRING,
    allowNull: false,
    get(this: BookInstance) {
      return this.getDataValue('author') + ' - ' + this.getDataValue('title');
    }
  },
  author: {
    type: S.STRING,
    allowNull: false,
    set(val: string){
      this.setDataValue('author', val.toLowerCase());
    }
  },
  userId:{ // User 表的外键
    type: S.INTEGER,
    references: {
      model: User
    }
  }
},{
    comment: "图书表", // 表注释
    indexes: [  // 表索引
      {
        fields: ['id']
      }
    ]
})

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

main 方法

async function main() {
  try {
    // await Book.sync();
    // await User.sync();

    Book.beforeValidate(() => {
      console.log("验证之前");
    });

    const book = Book.build({ status: "onSale", author: "yugo", description: "lean ts", title: "typescript" });
    await book.validate()

  }catch(e){
    // console.log(e)
    if (e instanceof Sequelize.ValidationError) {
      console.log(e.message);
    }
  }
  process.exit(0)
}

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

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

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

const book = Book.build({ status: "inSale", author: "yugo", description: "lean ts", title: "typescript" });
await book.save()
# Yugo @ Tractor in ~/Desktop/nl-sequelize/code/chapter2 on git:master x [13:27:55]
$ ts-node index.ts
验证之前
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,自己找一下即可。