diff --git a/packages/core/database/src/__tests__/collection.test.ts b/packages/core/database/src/__tests__/collection.test.ts index 7951a25d2..708fc39dd 100644 --- a/packages/core/database/src/__tests__/collection.test.ts +++ b/packages/core/database/src/__tests__/collection.test.ts @@ -97,7 +97,7 @@ describe('collection', () => { const field = collection.getField('name'); const r1 = await field.existsInDb(); expect(r1).toBe(true); - await field.removeFromDb(); + await collection.removeFieldFromDb('name'); const r2 = await field.existsInDb(); expect(r2).toBe(false); diff --git a/packages/core/database/src/collection.ts b/packages/core/database/src/collection.ts index eb9ed4b42..ad46b72f5 100644 --- a/packages/core/database/src/collection.ts +++ b/packages/core/database/src/collection.ts @@ -5,6 +5,7 @@ import { ModelOptions, ModelStatic, QueryInterfaceDropTableOptions, + QueryInterfaceOptions, SyncOptions, Transactionable, Utils, @@ -382,6 +383,84 @@ export class Collection< return this.context.database.removeCollection(this.name); } + async removeFieldFromDb(name: string, options?: QueryInterfaceOptions) { + const field = this.getField(name); + if (!field) { + return; + } + + const attribute = this.model.rawAttributes[name]; + + if (!attribute) { + field.remove(); + // console.log('field is not attribute'); + return; + } + + // @ts-ignore + if (this.isInherited() && this.parentFields().has(name)) { + return; + } + + if ((this.model as any)._virtualAttributes.has(this.name)) { + field.remove(); + // console.log('field is virtual attribute'); + return; + } + + if (this.model.primaryKeyAttributes.includes(this.name)) { + // 主键不能删除 + return; + } + + if (this.model.options.timestamps !== false) { + // timestamps 相关字段不删除 + let timestampsFields = ['createdAt', 'updatedAt', 'deletedAt']; + if (this.db.options.underscored) { + timestampsFields = timestampsFields.map((fieldName) => snakeCase(fieldName)); + } + if (timestampsFields.includes(field.columnName())) { + this.fields.delete(name); + return; + } + } + + // 排序字段通过 sortable 控制 + const sortable = this.options.sortable; + if (sortable) { + let sortField: any; + if (sortable === true) { + sortField = 'sort'; + } else if (typeof sortable === 'string') { + sortField = sortable; + } else if (sortable.name) { + sortField = sortable.name || 'sort'; + } + if (field.name === sortField) { + return; + } + } + + if (this.isView()) { + field.remove(); + return; + } + + const columnReferencesCount = _.filter(this.model.rawAttributes, (attr) => attr.field == field.columnName()).length; + + if ( + (await field.existsInDb({ + transaction: options?.transaction, + })) && + columnReferencesCount == 1 + ) { + const queryInterface = this.db.sequelize.getQueryInterface(); + await queryInterface.removeColumn(this.getTableNameWithSchema(), field.columnName(), options); + } + + field.remove(); + } + async removeFromDb(options?: QueryInterfaceDropTableOptions) { if ( !this.isView() && diff --git a/packages/core/database/src/fields/field.ts b/packages/core/database/src/fields/field.ts index 5e4910f46..f26b23617 100644 --- a/packages/core/database/src/fields/field.ts +++ b/packages/core/database/src/fields/field.ts @@ -1,15 +1,7 @@ import _ from 'lodash'; -import { - DataType, - ModelAttributeColumnOptions, - ModelIndexesOptions, - QueryInterfaceOptions, - SyncOptions, - Transactionable, -} from 'sequelize'; +import { DataType, ModelAttributeColumnOptions, ModelIndexesOptions, SyncOptions, Transactionable } from 'sequelize'; import { Collection } from '../collection'; import { Database } from '../database'; -import { InheritedCollection } from '../inherited-collection'; import { ModelEventTypes } from '../types'; import { snakeCase } from '../utils'; @@ -102,84 +94,6 @@ export abstract class Field { return this.name; } - async removeFromDb(options?: QueryInterfaceOptions) { - const attribute = this.collection.model.rawAttributes[this.name]; - - if (!attribute) { - this.remove(); - // console.log('field is not attribute'); - return; - } - - if (this.collection.isInherited() && (this.collection).parentFields().has(this.name)) { - return; - } - - if ((this.collection.model as any)._virtualAttributes.has(this.name)) { - this.remove(); - // console.log('field is virtual attribute'); - return; - } - if (this.collection.model.primaryKeyAttributes.includes(this.name)) { - // 主键不能删除 - return; - } - if (this.collection.model.options.timestamps !== false) { - // timestamps 相关字段不删除 - let timestampsFields = ['createdAt', 'updatedAt', 'deletedAt']; - if (this.database.options.underscored) { - timestampsFields = timestampsFields.map((field) => snakeCase(field)); - } - if (timestampsFields.includes(this.columnName())) { - this.collection.fields.delete(this.name); - return; - } - } - // 排序字段通过 sortable 控制 - const sortable = this.collection.options.sortable; - if (sortable) { - let sortField: any; - if (sortable === true) { - sortField = 'sort'; - } else if (typeof sortable === 'string') { - sortField = sortable; - } else if (sortable.name) { - sortField = sortable.name || 'sort'; - } - if (this.name === sortField) { - return; - } - } - - // if (this.options.field && this.name !== this.options.field) { - // // field 指向的是真实的字段名,如果与 name 不一样,说明字段只是引用 - // this.remove(); - // return; - // } - - if (this.collection.isView()) { - this.remove(); - return; - } - - const columnReferencesCount = _.filter( - this.collection.model.rawAttributes, - (attr) => attr.field == this.columnName(), - ).length; - - if ( - (await this.existsInDb({ - transaction: options?.transaction, - })) && - columnReferencesCount == 1 - ) { - const queryInterface = this.database.sequelize.getQueryInterface(); - await queryInterface.removeColumn(this.collection.getTableNameWithSchema(), this.columnName(), options); - } - - this.remove(); - } - async existsInDb(options?: Transactionable) { const opts = { transaction: options?.transaction, diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/server/models/field.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/server/models/field.ts index 43b16f989..462fb62ae 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/server/models/field.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/server/models/field.ts @@ -98,16 +98,12 @@ export class FieldModel extends MagicAttributeModel { async remove(options?: any) { const collection = this.getFieldCollection(); + if (!collection) { return; } - const field = collection.getField(this.get('name')); - if (!field) { - return; - } - - return field.removeFromDb({ + return collection.removeFieldFromDb(this.get('name'), { transaction: options.transaction, }); } diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/server/resourcers/collections.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/server/resourcers/collections.ts index f738816a8..67faa908e 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/server/resourcers/collections.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/server/resourcers/collections.ts @@ -70,6 +70,7 @@ export default { }); const collection = db.getCollection(filterByTk); + await collection.sync({ force: false, alter: {