Typescript Sequelize v5的模型加载器&;打字稿

Typescript Sequelize v5的模型加载器&;打字稿,typescript,express,sequelize.js,Typescript,Express,Sequelize.js,以前在项目(v4)中使用过Sequelize,但尝试使用Sequelize v5和Typescript启动新项目 我遵循Sequelize的文档,了解如何在以下位置定义模型: 我现在有一个工作的ORM,但是仅在导入实际模型以供使用时,而不是通过从模型加载器导入数据库。 i、 e.从“./db/models/User”导入{User} 导入数据库时,尝试访问db.User时只返回未定义的 试图弄清楚如何让模型加载器与Sequelize V5和Typescript一起放置在nice中,但目前它是空

以前在项目(v4)中使用过Sequelize,但尝试使用Sequelize v5和Typescript启动新项目

我遵循Sequelize的文档,了解如何在以下位置定义模型:

我现在有一个工作的ORM,但是仅在导入实际模型以供使用时,而不是通过从模型加载器导入数据库。
i、 e.
从“./db/models/User”导入{User}

导入数据库时,尝试访问db.User时只返回未定义的

试图弄清楚如何让模型加载器与Sequelize V5和Typescript一起放置在nice中,但目前它是空的

现在,我可以看出它在搜索.js文件。所以很明显,它不会拾取user.ts文件。将此更改为.ts将显示错误

    at Sequelize.import (/node_modules/sequelize/lib/sequelize.js:486:38)
    at fs_1.default.readdirSync.filter.forEach.file (/src/db/models/index.ts:26:35)
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (/src/db/models/index.ts:25:4)

src/db/models/index.ts
模型加载器

'use strict';

import fs from "fs";
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);

const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];

interface DB {
  [key: string]: any;
}

var db: DB = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

fs.readdirSync(__dirname)
  .filter(file => {
    return (
      file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
    );
  })
  .forEach(file => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });
// Important: creates associations based on associations defined in associate function in the model files
Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
再读


定义模型似乎有点清晰(但有点多余),但是在从index.js初始化Sequelize时如何调用这个init方法呢?

所以我已经让它工作了,但是是在一个非循环的模型加载器中。我忽略了定义文档,

对于冗长的课堂教学法:

我将经历建立2个模型及其关联的过程,希望能帮助那些试图将Typescript与Sequelize v5集成的人

肯定会喜欢这种方法的反馈。

让我们从用户类和相关标识(用于访问API)开始

/src/db/models/user.ts

import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { Association, HasManyGetAssociationsMixin, HasManyAddAssociationMixin, HasManyHasAssociationMixin, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin } from 'sequelize';
import { Identity } from './identity';
export class User extends Model {
  public id!: string; // Note that the `null assertion` `!` is required in strict mode.
  public active!: boolean;

  // timestamps!
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;

  public getIdentities!: HasManyGetAssociationsMixin<Identity>; // Note the null assertions!
  public addIdentity!: HasManyAddAssociationMixin<Identity, number>;
  public hasIdentity!: HasManyHasAssociationMixin<Identity, number>;
  public countIdentities!: HasManyCountAssociationsMixin;
  public createIdentity!: HasManyCreateAssociationMixin<Identity>;

  // You can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  public readonly identities?: Identity[]; // Note this is optional since it's only populated when explicitly requested in code

  public static associations: {
    identities: Association<User, Identity>;
  };

}

export function initUser(sequelize: Sequelize): void {
  User.init({
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
    },
    active: {
      type:DataTypes.BOOLEAN,
      defaultValue: true,
      allowNull: false
    }
  }, {
    tableName: 'User', 
    sequelize: sequelize, // this bit is important
  });


}

export function associateUser(): void {
  // Here we associate which actually populates out pre-declared `association` static and other methods.
  User.hasMany(Identity, {
    sourceKey: 'id',
    foreignKey: 'UserId',
    as: 'identities' // this determines the name in `associations`!
  });
}

import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { Association, HasOneGetAssociationMixin, HasOneCreateAssociationMixin } from 'sequelize';
import { User } from './user'

import * as bcrypt from "bcryptjs";

export class Identity extends Model {
  public id!: string; // Note that the `null assertion` `!` is required in strict mode.
  public username!: string;
  public password!: string;
  public UserId: string;
  public active!: boolean;

  // timestamps!
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;

  public getUser!: HasOneGetAssociationMixin<User>; // Note the null assertions!

  // You can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  public readonly user?: User; // Note this is optional since it's only populated when explicitly requested in code

  public static associations: {
    user: Association<Identity, User>;
  };

  public validatePassword(password: string) : boolean {
    return bcrypt.compareSync(password, this.password)
  }
}

export function initIdentity(sequelize: Sequelize): void {
  Identity.init({
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
    },
    username: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    },
    UserId: {
      type: DataTypes.UUID,
      allowNull: true
    },
    active: {
      type:DataTypes.BOOLEAN,
      defaultValue: true,
      allowNull: false
    }
  }, {
    tableName: 'Identity', 
    sequelize: sequelize, // this bit is important
  });

}

export function associateIdentity(): void {
  // Here we associate which actually populates out pre-declared `association` static and other methods.
  Identity.belongsTo(User, {targetKey: 'id'});
}


import { initUser, associateUser } from "./user";
import { initIdentity, associateIdentity } from "./identity";

const Sequelize = require('sequelize');

const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];


interface DB {
  [key: string]: any;
}

const sequelize = new Sequelize(config.database, config.username, config.password, config);

initUser(sequelize);
initIdentity(sequelize)

associateUser();
associateIdentity();

const db = {
  sequelize,
  Sequelize,
  User: sequelize.models.User,
  Identity: sequelize.models.Identity
}

module.exports = db;
接下来,我们声明了所有与Sequelize和数据库相关的“虚拟”成员和函数。此外还有
init
&&
associate函数,用于将所有内容连接在一起

注意您可能会注意到在
identity.ts
中,在关联中使用的是UserId而不是UserId。出于某种原因,它一直假设关联将通过UserId进行,即使我使用了UserId。在执行查询时,它抱怨没有列UserId(而是UserId)。因此,将其更新为大写字母“U”就解决了这个问题我不确定它为什么在此时这样做。

现在将其全部绑定起来

'use strict';

import fs from "fs";
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);

const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];

interface DB {
  [key: string]: any;
}

var db: DB = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

fs.readdirSync(__dirname)
  .filter(file => {
    return (
      file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
    );
  })
  .forEach(file => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });
// Important: creates associations based on associations defined in associate function in the model files
Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
/src/db/index.ts

import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { Association, HasManyGetAssociationsMixin, HasManyAddAssociationMixin, HasManyHasAssociationMixin, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin } from 'sequelize';
import { Identity } from './identity';
export class User extends Model {
  public id!: string; // Note that the `null assertion` `!` is required in strict mode.
  public active!: boolean;

  // timestamps!
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;

  public getIdentities!: HasManyGetAssociationsMixin<Identity>; // Note the null assertions!
  public addIdentity!: HasManyAddAssociationMixin<Identity, number>;
  public hasIdentity!: HasManyHasAssociationMixin<Identity, number>;
  public countIdentities!: HasManyCountAssociationsMixin;
  public createIdentity!: HasManyCreateAssociationMixin<Identity>;

  // You can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  public readonly identities?: Identity[]; // Note this is optional since it's only populated when explicitly requested in code

  public static associations: {
    identities: Association<User, Identity>;
  };

}

export function initUser(sequelize: Sequelize): void {
  User.init({
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
    },
    active: {
      type:DataTypes.BOOLEAN,
      defaultValue: true,
      allowNull: false
    }
  }, {
    tableName: 'User', 
    sequelize: sequelize, // this bit is important
  });


}

export function associateUser(): void {
  // Here we associate which actually populates out pre-declared `association` static and other methods.
  User.hasMany(Identity, {
    sourceKey: 'id',
    foreignKey: 'UserId',
    as: 'identities' // this determines the name in `associations`!
  });
}

import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { Association, HasOneGetAssociationMixin, HasOneCreateAssociationMixin } from 'sequelize';
import { User } from './user'

import * as bcrypt from "bcryptjs";

export class Identity extends Model {
  public id!: string; // Note that the `null assertion` `!` is required in strict mode.
  public username!: string;
  public password!: string;
  public UserId: string;
  public active!: boolean;

  // timestamps!
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;

  public getUser!: HasOneGetAssociationMixin<User>; // Note the null assertions!

  // You can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  public readonly user?: User; // Note this is optional since it's only populated when explicitly requested in code

  public static associations: {
    user: Association<Identity, User>;
  };

  public validatePassword(password: string) : boolean {
    return bcrypt.compareSync(password, this.password)
  }
}

export function initIdentity(sequelize: Sequelize): void {
  Identity.init({
    id: {
      type: DataTypes.UUID,
      primaryKey: true,
    },
    username: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    },
    UserId: {
      type: DataTypes.UUID,
      allowNull: true
    },
    active: {
      type:DataTypes.BOOLEAN,
      defaultValue: true,
      allowNull: false
    }
  }, {
    tableName: 'Identity', 
    sequelize: sequelize, // this bit is important
  });

}

export function associateIdentity(): void {
  // Here we associate which actually populates out pre-declared `association` static and other methods.
  Identity.belongsTo(User, {targetKey: 'id'});
}


import { initUser, associateUser } from "./user";
import { initIdentity, associateIdentity } from "./identity";

const Sequelize = require('sequelize');

const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];


interface DB {
  [key: string]: any;
}

const sequelize = new Sequelize(config.database, config.username, config.password, config);

initUser(sequelize);
initIdentity(sequelize)

associateUser();
associateIdentity();

const db = {
  sequelize,
  Sequelize,
  User: sequelize.models.User,
  Identity: sequelize.models.Identity
}

module.exports = db;
要完成的通常模型加载,转到目录,找到所有模型,然后将它们导入sequelize。现在就像我之前说过的,尝试在模型类中使用
define
会在尝试使用这个模型加载器时产生问题,因为非Typescript版本总是查找*.js而不是*.ts。更改为*.ts会使
define
调用中的所有内容崩溃。(更不用说,因为所有这些代码都将被传输到js文件中,这难道不会导致另一个问题吗?)

但正如你所看到的,我做每件事都是手工的,而不是循环的。可能有更好的循环方式来实现这一点,但目前这已经足够了

模型在sequelize中通过调用其
init
函数进行初始化。初始化后,通过
associate

在启动express服务器之前,我需要索引文件,这一切都会启动。轰

关于我的方法需要注意的其他事项 我不想安装比我需要的更多的软件包。因此,我避开了sequelize typescript和sequelize typescript cli。这意味着我所有的种子文件和迁移文件都需要手工制作,而不需要使用cli(这真的没那么糟糕),它们不是*.ts而是*.js

例如:
20191017135846创建标识.js

'use strict'
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable({tableName:'Identity'}, {
      id: {
        type: Sequelize.UUID,
        defaultValue: Sequelize.UUIDV4,
        allowNull: false,
        autoIncrement: false,
        primaryKey: true,
      },
      username: {
        type: Sequelize.STRING,
        allowNull: false,
        unique: true,
      },
      password: {
        type: Sequelize.STRING,
        allowNull: false,
      },
      UserId: {
        type: Sequelize.UUID,
        references: {
          model: 'User', // name of Target model
          key: 'id', // key in Target model that we're referencing
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      active: {
        type: Sequelize.BOOLEAN,
        defaultValue: true,
        allowNull: false,
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
      },
    })
  },
  down: (queryInterface) => {
    return queryInterface.dropTable({tableName:'Identity', schema:'public'})
  }
}
'use strict'
var moment = require('moment');
var uuidv4 = require('uuid/v4');
const bcrypt = require('bcryptjs');

module.exports = {
  up: async (queryInterface) => {   
      // User
      const user1Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user1Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user1",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user1Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )

      const user2Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user2Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user2",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user2Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )

      const user3Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user3Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user3",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user3Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )


  },
  down: async (queryInterface) => {
    await queryInterface.bulkDelete({ tableName: 'User'}, null, {})
  }
}
20191015141822种子用户.js

'use strict'
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable({tableName:'Identity'}, {
      id: {
        type: Sequelize.UUID,
        defaultValue: Sequelize.UUIDV4,
        allowNull: false,
        autoIncrement: false,
        primaryKey: true,
      },
      username: {
        type: Sequelize.STRING,
        allowNull: false,
        unique: true,
      },
      password: {
        type: Sequelize.STRING,
        allowNull: false,
      },
      UserId: {
        type: Sequelize.UUID,
        references: {
          model: 'User', // name of Target model
          key: 'id', // key in Target model that we're referencing
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      active: {
        type: Sequelize.BOOLEAN,
        defaultValue: true,
        allowNull: false,
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
      },
    })
  },
  down: (queryInterface) => {
    return queryInterface.dropTable({tableName:'Identity', schema:'public'})
  }
}
'use strict'
var moment = require('moment');
var uuidv4 = require('uuid/v4');
const bcrypt = require('bcryptjs');

module.exports = {
  up: async (queryInterface) => {   
      // User
      const user1Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user1Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user1",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user1Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )

      const user2Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user2Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user2",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user2Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )

      const user3Id = uuidv4();
      await queryInterface.bulkInsert('User', 
        [
          {
            id:user3Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )
      await queryInterface.bulkInsert('Identity', 
        [
          {
            id:uuidv4(),
            username: "user3",
            password: bcrypt.hashSync('password', bcrypt.genSaltSync(10)),
            UserId: user3Id,
            createdAt: new Date( moment.utc().format() ), 
            updatedAt: new Date( moment.utc().format() )
          }
        ], 
      )


  },
  down: async (queryInterface) => {
    await queryInterface.bulkDelete({ tableName: 'User'}, null, {})
  }
}
在这一点上,你可以运行

sequelize db:migrate
sequelize db:seed:all
一切正常,可以访问数据库

现在使用classes/typescript,我注意到将模型添加到和导出的db对象是多余的

我可以通过导入访问所需的模型

从“../db/models/User”导入{User} 或 需要(“./db/models/index”)


然后,我可以使用User.findAll()或其他import db.User.findAll()执行此操作。

感谢您抽出时间回答您自己的问题。你想要反馈,我也想给你反馈,但不是现在,我太忙了。几周后你能提醒我吗?欢迎在这里或github问题(@papb)上点击我。