Model Loader for Sequelize v5 & Typescript



以前在项目(v4)中使用过Sequelize,但尝试使用Sequelize v5&打字

关于如何在以下位置定义模型,我遵循了Sequelize的文档:https://sequelize.org/master/manual/typescript.html#usage-的代码序列定义代码-

我现在有一个正在工作的ORM,但仅在导入实际模型时使用,而不是通过从模型加载程序导入数据库
import { User } from "../db/models/user";

导入数据库时,在尝试访问数据库时只返回undefined。使用者

试图弄清楚如何用Sequelize V5和Typescript将模型加载程序放置得很好,但目前它是空的。

现在,我可以看出它在搜索.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)

我一直试图从网络搜索中得到一个明确的答案,但似乎一无所获。试着让一切都表现得很好,这已经够让人头疼的了。。。。在这一点上,我将迁移/种子程序作为js文件运行,因为我不想处理sequelize typescript clisequelize typescript

src/db/models/user.ts用户模型

import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { HasManyGetAssociationsMixin, HasManyAddAssociationMixin, HasManyHasAssociationMixin, Association, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin } from 'sequelize';
const db = require('./index')
import * as bcrypt from "bcryptjs";
export interface UserAttributes extends Model {
id: string;
email: string;
username: string;
password: string;
createdAt: Date;
updatedAt: Date;
validatePassword(password: string): boolean;
generateHash(password: string): string;
}
export type UserModel = typeof Model & {
new (): UserAttributes;
};
export const User = <UserModel>db.sequelize.define("User", {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
}
},
{
tableName: "User",
freezeTableName: true,
});
User.prototype.validatePassword = function (password: string) {
return bcrypt.compareSync(password, this.password)
}
User.prototype.generateHash = function (password: string) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10))
}

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;

进一步阅读https://sequelize.org/master/manual/typescript.html#usage

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

所以我已经让它工作了,但在一个非循环的模型加载器中。我忽略了定义文档,https://sequelize.org/master/manual/typescript.html#usage-的代码序列定义代码-

对于这里冗长的课堂方法:https://sequelize.org/master/manual/typescript.html#usage

我将介绍建立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`!
});
}

/src/db/models/identity.ts

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'});
}

因此,在此之后,我们声明了所有与Sequelize和数据库相关的"虚拟"成员和函数。此外还有CCD_ 6&amp;associate<model>函数,用于将所有内容连接在一起。

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

现在将其结合在一起

/src/db/index.ts

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文件,这不是会导致问题的另一种方式吗?)

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

通过调用它们的init<model>函数,依次初始化模型。初始化后,通过associate<model>的函数调用创建它们的关联

在启动我的express服务器之前,我需要索引文件,这一切都开始了。

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

示例:20191017135846-create-identity.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'})
}
}

20191015141822-seed-users.js

'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对象是多余的。。。。

我可以通过进口访问所需的模型

从".."导入{User}/数据库/模型/用户或需要('./db/models/index')

然后我可以使用User.findAll()或其他导入数据库来执行此操作。User.findAll()

最新更新