mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 14:16:26 +00:00
feat: belongs to many on delete (#1158)
This commit is contained in:
parent
4085ed0db7
commit
78fe77e2cf
@ -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;
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user