Итак, у меня все работает, но в циклическом загрузчике моделей. Я проигнорировал определенную документацию, https://sequelize.org/master/manual/typescript.html#usage-of--code-sequelize-define--code-
для подхода с длинными классами здесь: 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 и базе данных. Кроме того, есть функции init<model>
&& 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;
Обычная загрузка моделей, которую нужно выполнить, зашел в каталог, нашел все модели и затем импортировал их в секвелиз. Теперь, как я уже говорил ранее, попытка использовать define
в классе модели вызвала проблемы при попытке пройти через этот загрузчик модели, потому что не версии Typescript всегда искали * .js, а не * .ts. Изменение на * .ts привело к сбою при вызове define
. (Не говоря уже о том, что весь этот код будет перенесен в js-файлы, разве это не вызовет проблему наоборот?)
Но, как вы можете видеть, я делаю все вручную, а не в цикле,Возможно, есть лучший способ сделать это, но на данный момент этого будет достаточно.
Модели инициализируются последовательно с вызовами их функций init<model>
. После инициализации их ассоциации создаются с помощью вызова функции associate<model>
Перед запуском моего экспресс-сервера мне требуется файл индекса, и все это запускается. Boom.
Другие замечания, касающиеся моего подхода Я не хотел устанавливать больше пакетов, чем мне было нужно. Так что я избежал от sequelize-typcript и sequelize-typescript-cli. Это означает, что все мои файлы seeder и файлы миграции должны быть сделаны вручную без использования 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
, и все работает и может получить доступ к БД.
Теперь, используя классы / машинопись, я заметил, что добавление моделей в и экспортированный объект БД является излишним ....
Я могу получить доступ к нужным моделям через импорт
import {User} из '../db/models/user' или require ('./ db / models / index')
, что я могу затем сделать User.findAll () или с другим импортом db.User. FindAll ()