Skip to content

Commit f25f2c9

Browse files
Merge pull request #42 from HackedByChinese/feature/issue-34-association-options
allow additional options to be specified for associations - closes #42
2 parents 76b2920 + 8fc9138 commit f25f2c9

File tree

10 files changed

+402
-27
lines changed

10 files changed

+402
-27
lines changed

lib/annotations/ForeignKey.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
import {AssociationForeignKeyOptions} from 'sequelize';
12
import {Model} from "../models/Model";
23
import {addForeignKey} from "../services/association";
34

4-
export function ForeignKey(relatedClassGetter: () => typeof Model): Function {
5+
export function ForeignKey(relatedClassGetter: () => typeof Model, options?: AssociationForeignKeyOptions): Function {
56

67
return (target: any, propertyName: string) => {
78

8-
addForeignKey(target, relatedClassGetter, propertyName);
9+
if (!options) {
10+
options = {name: propertyName};
11+
} else if (!options.name) {
12+
options.name = propertyName;
13+
}
14+
15+
addForeignKey(target, relatedClassGetter, options);
916
};
1017
}
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
import {AssociationOptionsBelongsTo} from 'sequelize';
2+
13
import {Model} from "../../models/Model";
24
import {BELONGS_TO, addAssociation} from "../../services/association";
35

46
export function BelongsTo(relatedClassGetter: () => typeof Model,
5-
foreignKey?: string): Function {
7+
options?: string | AssociationOptionsBelongsTo): Function {
68

79
return (target: any, propertyName: string) => {
810

11+
if (typeof options === 'string') {
12+
options = {foreignKey: {name: options}};
13+
}
14+
915
addAssociation(
1016
target,
1117
BELONGS_TO,
1218
relatedClassGetter,
1319
propertyName,
14-
foreignKey
20+
options
1521
);
1622
};
1723
}

lib/annotations/association/BelongsToMany.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1+
import {AssociationOptionsBelongsToMany} from 'sequelize';
2+
13
import {Model} from "../../models/Model";
24
import {BELONGS_TO_MANY, addAssociation} from "../../services/association";
35

46
export function BelongsToMany(relatedClassGetter: () => typeof Model,
57
through: (() => typeof Model)|string,
6-
foreignKey?: string,
8+
options?: string | AssociationOptionsBelongsToMany,
79
otherKey?: string): Function {
810

911
return (target: any, propertyName: string) => {
10-
12+
if (typeof options === 'string') {
13+
// don't worry, through is mainly here to avoid TS error; actual through value is resolved later
14+
options = {through: '', foreignKey: {name: options}};
15+
}
1116
addAssociation(
1217
target,
1318
BELONGS_TO_MANY,
1419
relatedClassGetter,
1520
propertyName,
16-
foreignKey,
21+
options,
1722
otherKey,
1823
through
1924
);
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import {AssociationOptionsHasMany} from 'sequelize';
2+
13
import {Model} from "../../models/Model";
24
import {HAS_MANY, addAssociation} from "../../services/association";
35

46
export function HasMany(relatedClassGetter: () => typeof Model,
5-
foreignKey?: string): Function {
7+
options?: string | AssociationOptionsHasMany): Function {
68

79
return (target: any, propertyName: string) => {
8-
10+
if (typeof options === 'string') {
11+
options = {foreignKey: {name: options}};
12+
}
913
addAssociation(
1014
target,
1115
HAS_MANY,
1216
relatedClassGetter,
1317
propertyName,
14-
foreignKey
18+
options
1519
);
1620
};
1721
}

lib/annotations/association/HasOne.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import {AssociationOptionsHasOne} from 'sequelize';
2+
13
import {Model} from "../../models/Model";
24
import {addAssociation, HAS_ONE} from "../../services/association";
35

46
export function HasOne(relatedClassGetter: () => typeof Model,
5-
foreignKey?: string): Function {
7+
options?: string | AssociationOptionsHasOne): Function {
68

79
return (target: any, propertyName: string) => {
8-
10+
if (typeof options === 'string') {
11+
options = {foreignKey: {name: options}};
12+
}
913
addAssociation(
1014
target,
1115
HAS_ONE,
1216
relatedClassGetter,
1317
propertyName,
14-
foreignKey
18+
options
1519
);
1620
};
1721
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {AssociationOptionsBelongsTo, AssociationOptionsBelongsToMany, AssociationOptionsHasMany,
2+
AssociationOptionsHasOne, AssociationOptionsManyToMany} from 'sequelize';
13
import {Model} from "../models/Model";
24

35
export interface ISequelizeAssociation {
@@ -6,7 +8,8 @@ export interface ISequelizeAssociation {
68
relatedClassGetter: () => typeof Model;
79
through?: string;
810
throughClassGetter?: () => typeof Model;
9-
foreignKey?: string;
11+
options?: AssociationOptionsBelongsTo | AssociationOptionsBelongsToMany | AssociationOptionsHasMany |
12+
AssociationOptionsHasOne | AssociationOptionsManyToMany;
1013
otherKey?: string;
1114
as: string;
1215
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import {AssociationForeignKeyOptions} from 'sequelize';
12
import {Model} from "../models/Model";
23

34
export interface ISequelizeForeignKeyConfig {
45

56
relatedClassGetter: () => typeof Model;
6-
foreignKey: string;
7+
options: string | AssociationForeignKeyOptions;
78
}

lib/models/BaseSequelize.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {merge} from 'lodash';
12
import {Model} from "./Model";
23
import {getModels} from "../services/models";
34
import {getAssociations, BELONGS_TO_MANY} from "../services/association";
@@ -89,7 +90,6 @@ export abstract class BaseSequelize {
8990

9091
associations.forEach(association => {
9192

92-
const foreignKey = association.foreignKey || getForeignKey(model, association);
9393
const relatedClass = association.relatedClassGetter();
9494
let through;
9595
let otherKey;
@@ -126,12 +126,17 @@ export abstract class BaseSequelize {
126126
}
127127
}
128128

129-
model[association.relation](relatedClass, {
129+
// ensure association options by default have most explicit foreignKey options
130+
// so it merges properly with different foreignKey option permutations, or is overrwritten
131+
// completely by options.foreignKey
132+
const foreignKey = getForeignKey(model, association);
133+
const options = merge({foreignKey: {name: foreignKey}}, association.options,
134+
{
130135
as: association.as,
131136
through,
132-
foreignKey,
133137
otherKey
134138
});
139+
model[association.relation](relatedClass, options);
135140

136141
// The associations has to be adjusted
137142
const _association = model['associations'][association.as];

lib/services/association.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import 'reflect-metadata';
2+
import {AssociationOptions, AssociationForeignKeyOptions, AssociationOptionsBelongsTo, AssociationOptionsBelongsToMany,
3+
AssociationOptionsHasMany, AssociationOptionsHasOne, AssociationOptionsManyToMany} from 'sequelize';
24
import {Model} from "../models/Model";
35
import {ISequelizeForeignKeyConfig} from "../interfaces/ISequelizeForeignKeyConfig";
46
import {ISequelizeAssociation} from "../interfaces/ISequelizeAssociation";
@@ -18,7 +20,9 @@ export function addAssociation(target: any,
1820
relation: string,
1921
relatedClassGetter: () => typeof Model,
2022
as: string,
21-
foreignKey?: string,
23+
options?: AssociationOptionsBelongsTo |
24+
AssociationOptionsBelongsToMany | AssociationOptionsHasMany |
25+
AssociationOptionsHasOne | AssociationOptionsManyToMany,
2226
otherKey?: string,
2327
through?: (() => typeof Model)|string): void {
2428

@@ -42,7 +46,7 @@ export function addAssociation(target: any,
4246
throughClassGetter,
4347
through: through as string,
4448
as,
45-
foreignKey,
49+
options,
4650
otherKey
4751
});
4852
}
@@ -52,11 +56,19 @@ export function addAssociation(target: any,
5256
*/
5357
export function getForeignKey(_class: typeof Model,
5458
association: ISequelizeAssociation): string {
59+
const options = association.options as AssociationOptions;
5560

56-
// if foreign key is defined return this one
57-
if (association.foreignKey) {
61+
if (options && options.foreignKey) {
62+
const foreignKey = options.foreignKey;
63+
// if options is an object and has a string foreignKey property, use that as the name
64+
if (typeof foreignKey === 'string') {
65+
return foreignKey;
66+
}
5867

59-
return association.foreignKey;
68+
// if options is an object with foreignKey.name, use that as the name
69+
if (foreignKey.name) {
70+
return foreignKey.name;
71+
}
6072
}
6173

6274
// otherwise calculate the foreign key by related or through class
@@ -90,8 +102,10 @@ export function getForeignKey(_class: typeof Model,
90102
for (const foreignKey of foreignKeys) {
91103

92104
if (foreignKey.relatedClassGetter() === relatedClass) {
93-
94-
return foreignKey.foreignKey;
105+
if (typeof foreignKey.options === 'string') {
106+
return foreignKey.options;
107+
}
108+
return (foreignKey.options as any).name;
95109
}
96110
}
97111

@@ -123,7 +137,7 @@ export function getAssociationsByRelation(target: any, relatedClass: any): ISequ
123137
*/
124138
export function addForeignKey(target: any,
125139
relatedClassGetter: () => typeof Model,
126-
attrName: string): void {
140+
options: string | AssociationForeignKeyOptions): void {
127141

128142
let foreignKeys = getForeignKeys(target);
129143

@@ -134,7 +148,7 @@ export function addForeignKey(target: any,
134148

135149
foreignKeys.push({
136150
relatedClassGetter,
137-
foreignKey: attrName
151+
options
138152
});
139153
}
140154

0 commit comments

Comments
 (0)