feat: belongs to many on delete (#1158)

This commit is contained in:
ChengLei Shao 2022-11-29 18:27:41 +08:00 committed by GitHub
parent 4085ed0db7
commit 78fe77e2cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 6 deletions

View File

@ -1,5 +1,5 @@
import { omit } from 'lodash'; import { omit } from 'lodash';
import { BelongsToOptions as SequelizeBelongsToOptions, Utils } from 'sequelize'; import { BelongsTo, BelongsToOptions as SequelizeBelongsToOptions, Utils } from 'sequelize';
import { Reference } from '../features/ReferencesMap'; import { Reference } from '../features/ReferencesMap';
import { checkIdentifier } from '../utils'; import { checkIdentifier } from '../utils';
import { BaseRelationFieldOptions, RelationField } from './relation-field'; import { BaseRelationFieldOptions, RelationField } from './relation-field';
@ -16,18 +16,22 @@ export class BelongsToField extends RelationField {
return target || Utils.pluralize(name); return target || Utils.pluralize(name);
} }
reference(association): Reference { static toReference(db, association, onDelete) {
const targetKey = association.targetKey; const targetKey = association.targetKey;
return { return {
sourceCollectionName: this.database.modelCollection.get(association.source).name, sourceCollectionName: db.modelCollection.get(association.source).name,
sourceField: association.foreignKey, sourceField: association.foreignKey,
targetField: targetKey, targetField: targetKey,
targetCollectionName: this.database.modelCollection.get(association.target).name, targetCollectionName: db.modelCollection.get(association.target).name,
onDelete: this.options.onDelete, onDelete: onDelete,
}; };
} }
reference(association): Reference {
return BelongsToField.toReference(this.database, association, this.options.onDelete);
}
bind() { bind() {
const { database, collection } = this.context; const { database, collection } = this.context;
const Target = this.TargetModel; const Target = this.TargetModel;

View File

@ -1,7 +1,9 @@
import { omit } from 'lodash'; import { omit } from 'lodash';
import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize'; import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize';
import { Collection } from '../collection'; import { Collection } from '../collection';
import { Reference } from '../features/ReferencesMap';
import { checkIdentifier } from '../utils'; import { checkIdentifier } from '../utils';
import { BelongsToField } from './belongs-to-field';
import { MultipleRelationFieldOptions, RelationField } from './relation-field'; import { MultipleRelationFieldOptions, RelationField } from './relation-field';
export class BelongsToManyField extends RelationField { export class BelongsToManyField extends RelationField {
@ -25,6 +27,15 @@ export class BelongsToManyField extends RelationField {
return this.options.otherKey; return this.options.otherKey;
} }
references(association): Reference[] {
const db = this.context.database;
return [
BelongsToField.toReference(db, association.toSource, this.options.onDelete),
BelongsToField.toReference(db, association.toTarget, this.options.onDelete),
];
}
bind() { bind() {
const { database, collection } = this.context; const { database, collection } = this.context;
@ -86,6 +97,8 @@ export class BelongsToManyField extends RelationField {
Through.addIndex([this.options.foreignKey]); Through.addIndex([this.options.foreignKey]);
Through.addIndex([this.options.otherKey]); Through.addIndex([this.options.otherKey]);
this.references(association).forEach((reference) => this.database.referenceMap.addReference(reference));
return true; return true;
} }
@ -97,6 +110,11 @@ export class BelongsToManyField extends RelationField {
database.removePendingField(this); database.removePendingField(this);
// 删掉 model 的关联字段 // 删掉 model 的关联字段
const association = collection.model.associations[this.name];
if (association) {
this.references(association).forEach((reference) => this.database.referenceMap.removeReference(reference));
}
this.clearAccessors(); this.clearAccessors();
delete collection.model.associations[this.name]; delete collection.model.associations[this.name];
} }

View File

@ -13,6 +13,58 @@ describe('reference integrity check', () => {
await app.destroy(); await app.destroy();
}); });
it('should delete cascade on belongs to many relation', async () => {
const posts = db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsToMany', name: 'tags', onDelete: 'CASCADE' },
],
});
const tags = db.collection({
name: 'tags',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'belongsToMany',
name: 'posts',
onDelete: 'CASCADE',
},
],
});
await db.sync();
const post = await posts.repository.create({
values: {
title: 'post',
tags: [{ name: 't1' }, { name: 't2' }],
},
});
// @ts-ignore
const throughModel = posts.model.associations.tags.through.model;
expect(await throughModel.count()).toEqual(2);
await post.destroy();
expect(await throughModel.count()).toEqual(0);
expect(db.referenceMap.getReferences('posts').length > 0).toBeTruthy();
expect(db.referenceMap.getReferences('tags').length > 0).toBeTruthy();
tags.removeField('posts');
tags.removeField('tags');
expect(db.referenceMap.getReferences('posts').length == 0).toBeTruthy();
expect(db.referenceMap.getReferences('tags').length == 0).toBeTruthy();
});
it('should clean reference after collection destroy', async () => { it('should clean reference after collection destroy', async () => {
const users = db.collection({ const users = db.collection({
name: 'users', name: 'users',

@ -1 +1 @@
Subproject commit 67f51c7fdd132ab12821f31f737b6f0cfb70d212 Subproject commit 5a5fba41ae1cecaf3b073fcb610e9dc68dad0b85