chore: remove field from db (#3233)

* chore: remove field from db

* fix: test

* chore: test
This commit is contained in:
ChengLei Shao 2023-12-19 20:49:34 +08:00 committed by GitHub
parent a8ae078baa
commit 2289bb7418
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 94 deletions

View File

@ -97,7 +97,7 @@ describe('collection', () => {
const field = collection.getField('name'); const field = collection.getField('name');
const r1 = await field.existsInDb(); const r1 = await field.existsInDb();
expect(r1).toBe(true); expect(r1).toBe(true);
await field.removeFromDb(); await collection.removeFieldFromDb('name');
const r2 = await field.existsInDb(); const r2 = await field.existsInDb();
expect(r2).toBe(false); expect(r2).toBe(false);

View File

@ -5,6 +5,7 @@ import {
ModelOptions, ModelOptions,
ModelStatic, ModelStatic,
QueryInterfaceDropTableOptions, QueryInterfaceDropTableOptions,
QueryInterfaceOptions,
SyncOptions, SyncOptions,
Transactionable, Transactionable,
Utils, Utils,
@ -382,6 +383,84 @@ export class Collection<
return this.context.database.removeCollection(this.name); 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) { async removeFromDb(options?: QueryInterfaceDropTableOptions) {
if ( if (
!this.isView() && !this.isView() &&

View File

@ -1,15 +1,7 @@
import _ from 'lodash'; import _ from 'lodash';
import { import { DataType, ModelAttributeColumnOptions, ModelIndexesOptions, SyncOptions, Transactionable } from 'sequelize';
DataType,
ModelAttributeColumnOptions,
ModelIndexesOptions,
QueryInterfaceOptions,
SyncOptions,
Transactionable,
} from 'sequelize';
import { Collection } from '../collection'; import { Collection } from '../collection';
import { Database } from '../database'; import { Database } from '../database';
import { InheritedCollection } from '../inherited-collection';
import { ModelEventTypes } from '../types'; import { ModelEventTypes } from '../types';
import { snakeCase } from '../utils'; import { snakeCase } from '../utils';
@ -102,84 +94,6 @@ export abstract class Field {
return this.name; 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() && (<InheritedCollection>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) { async existsInDb(options?: Transactionable) {
const opts = { const opts = {
transaction: options?.transaction, transaction: options?.transaction,

View File

@ -98,16 +98,12 @@ export class FieldModel extends MagicAttributeModel {
async remove(options?: any) { async remove(options?: any) {
const collection = this.getFieldCollection(); const collection = this.getFieldCollection();
if (!collection) { if (!collection) {
return; return;
} }
const field = collection.getField(this.get('name')); return collection.removeFieldFromDb(this.get('name'), {
if (!field) {
return;
}
return field.removeFromDb({
transaction: options.transaction, transaction: options.transaction,
}); });
} }

View File

@ -70,6 +70,7 @@ export default {
}); });
const collection = db.getCollection(filterByTk); const collection = db.getCollection(filterByTk);
await collection.sync({ await collection.sync({
force: false, force: false,
alter: { alter: {