User.ts
3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import {
BelongsTo,
BelongsToCreateAssociationMixin,
BelongsToGetAssociationMixin,
BelongsToSetAssociationMixin,
DataTypes,
FindOptions,
Model,
ModelCtor,
Op,
Optional
} from 'sequelize';
import { sequelize } from '../connection';
export interface UserAttributes {
id: number;
username: string;
firstName: string;
lastName: string;
groupId: number;
}
/**
* In this case, we make most fields optional. In real cases,
* only fields that have default/autoincrement values should be made optional.
*/
export interface UserCreationAttributes extends Optional<UserAttributes, 'id' | 'username' | 'lastName' | 'groupId'> {}
export class User extends Model<UserAttributes, UserCreationAttributes> implements UserAttributes {
public static associations: {
group: BelongsTo<User, UserGroup>;
};
public id!: number;
public username!: string;
public firstName!: string;
public lastName!: string;
public groupId!: number;
public createdAt!: Date;
public updatedAt!: Date;
// mixins for association (optional)
public group?: UserGroup;
public getGroup!: BelongsToGetAssociationMixin<UserGroup>;
public setGroup!: BelongsToSetAssociationMixin<UserGroup, number>;
public createGroup!: BelongsToCreateAssociationMixin<UserGroup>;
}
User.init(
{
id: {
type: DataTypes.NUMBER,
primaryKey: true,
},
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
username: DataTypes.STRING,
groupId: DataTypes.NUMBER,
},
{
version: true,
getterMethods: {
a() {
return 1;
},
},
setterMethods: {
b(val: string) {
this.username = val;
},
},
scopes: {
custom(a: number) {
return {
where: {
firstName: a,
},
};
},
custom2() {
return {}
}
},
indexes: [{
fields: ['firstName'],
using: 'BTREE',
name: 'firstNameIdx',
concurrently: true,
}],
sequelize,
}
);
User.afterSync(() => {
sequelize.getQueryInterface().addIndex(User.tableName, {
fields: ['lastName'],
using: 'BTREE',
name: 'lastNameIdx',
concurrently: true,
})
})
// Hooks
User.afterFind((users, options) => {
console.log('found');
});
// TODO: VSCode shows the typing being correctly narrowed but doesn't do it correctly
User.addHook('beforeFind', 'test', (options: FindOptions<UserAttributes>) => {
return undefined;
});
User.addHook('afterDestroy', async (instance, options) => {
// `options` from `afterDestroy` should be passable to `sequelize.transaction`
await instance.sequelize.transaction(options, async () => undefined);
});
// Model#addScope
User.addScope('withoutFirstName', {
where: {
firstName: {
[Op.is]: null,
},
},
});
User.addScope(
'withFirstName',
(firstName: string) => ({
where: { firstName },
}),
);
// associate
// it is important to import _after_ the model above is already exported so the circular reference works.
import { UserGroup } from './UserGroup';
export const Group = User.belongsTo(UserGroup, { as: 'group', foreignKey: 'groupId' });
// associations refer to their Model
const userType: ModelCtor<User> = User.associations.group.source;
const groupType: ModelCtor<UserGroup> = User.associations.group.target;
User.scope([
'custom2',
{ method: [ 'custom', 32 ] }
])
const instance = new User({ username: 'foo', firstName: 'bar', lastName: 'baz' });
instance.isSoftDeleted()