fix: allow set null with updateAssociations

This commit is contained in:
chenos 2021-01-02 22:30:13 +08:00
parent 752de2b58a
commit eafdddcd4d
3 changed files with 135 additions and 12 deletions

View File

@ -4,7 +4,9 @@ import { Dialect } from 'sequelize';
function getTestKey() { function getTestKey() {
const { id } = require.main; const { id } = require.main;
const key = id const key = id
.replace(process.env.PWD, '') // .replace(process.env.PWD, '')
.replace(`${process.env.PWD}/packages`, '')
.replace(/src\/__tests__/g, '')
.replace('.test.ts', '') .replace('.test.ts', '')
.replace(/[^\w]/g, '_'); .replace(/[^\w]/g, '_');
return key return key
@ -32,6 +34,9 @@ export function getDatabase() {
...config, ...config,
sync: { sync: {
force: true, force: true,
alter: {
drop: true,
}
}, },
hooks: { hooks: {
beforeDefine(columns, model) { beforeDefine(columns, model) {

View File

@ -0,0 +1,107 @@
import { getDatabase } from '..';
import Database from '../..';
let db: Database;
beforeEach(async () => {
db = getDatabase();
db.table({
name: 'users',
fields: [
{
type: 'hasMany',
name: 'posts',
},
{
type: 'hasOne',
name: 'profile',
}
],
});
db.table({
name: 'profiles',
});
db.table({
name: 'posts',
fields: [
{
type: 'hasMany',
name: 'comments',
},
{
type: 'belongsTo',
name: 'user',
},
{
type: 'belongsToMany',
name: 'tags',
}
],
});
db.table({
name: 'tags',
fields: [
{
type: 'belongsToMany',
name: 'posts',
}
]
});
db.table({
name: 'comments',
fields: [
{
type: 'belongsTo',
name: 'post',
},
]
});
await db.sync();
});
afterEach(async () => {
await db.close();
});
describe('updateAssociations', () => {
describe('set null', () => {
it('belongsTo', async () => {
const [User, Post] = db.getModels(['users', 'posts']);
const user = await User.create();
const post = await Post.create();
await post.updateAssociations({
user,
});
await post.updateAssociations({});
const postUser = await post.getUser();
expect(postUser.id).toBe(user.id);
await post.updateAssociations({
user: null,
});
expect(await post.getUser()).toBeNull();
});
it('hasMany', async () => {
const [User, Post] = db.getModels(['users', 'posts']);
const user = await User.create();
const post = await Post.create();
await user.updateAssociations({
posts: [post],
});
expect(await user.countPosts()).toBe(1);
await user.updateAssociations({
posts: [], // 空数组
});
expect(await user.countPosts()).toBe(0);
await user.updateAssociations({
posts: [post],
});
expect(await user.countPosts()).toBe(1);
await user.updateAssociations({
posts: null,
});
expect(await user.countPosts()).toBe(0);
});
});
});

View File

@ -137,6 +137,10 @@ export const DEFAULT_OFFSET = 0;
export const DEFAULT_LIMIT = 100; export const DEFAULT_LIMIT = 100;
export const MAX_LIMIT = 500; export const MAX_LIMIT = 500;
export interface UpdateAssociationOptions extends SaveOptions {
context?: any;
}
/** /**
* Model * Model
* *
@ -281,7 +285,7 @@ export abstract class Model extends SequelizeModel {
return where; return where;
} }
async updateSingleAssociation(key: string, data: any, options: SaveOptions<any> & { context?: any; } = {}) { async updateSingleAssociation(key: string, data: any, options: UpdateAssociationOptions = {}) {
const { const {
fields, fields,
transaction = await this.sequelize.transaction(), transaction = await this.sequelize.transaction(),
@ -293,6 +297,11 @@ export abstract class Model extends SequelizeModel {
const association = table.getAssociations().get(key); const association = table.getAssociations().get(key);
const accessors = association.getAccessors(); const accessors = association.getAccessors();
if (data == null) {
await this[accessors.set](null, opts);
return;
}
if (typeof data === 'number' || typeof data === 'string' || data instanceof SequelizeModel) { if (typeof data === 'number' || typeof data === 'string' || data instanceof SequelizeModel) {
await this[accessors.set](data, opts); await this[accessors.set](data, opts);
} else if (typeof data === 'object') { } else if (typeof data === 'object') {
@ -323,11 +332,8 @@ export abstract class Model extends SequelizeModel {
} }
} }
async updateMultipleAssociation(associationName: string, data: any, options: SaveOptions<any> & { context?: any; } = {}) { async updateMultipleAssociation(associationName: string, data: any, options: UpdateAssociationOptions = {}) {
const items = Array.isArray(data) ? data : [data]; const items = Array.isArray(data) ? data : data == null ? [] : [data];
if (!items.length) {
return;
}
const { const {
fields, fields,
@ -339,6 +345,12 @@ export abstract class Model extends SequelizeModel {
const table = this.database.getTable(this.constructor.name); const table = this.database.getTable(this.constructor.name);
const association = table.getAssociations().get(associationName); const association = table.getAssociations().get(associationName);
const accessors = association.getAccessors(); const accessors = association.getAccessors();
if (!items.length) {
await this[accessors.set](null, opts);
return;
}
const Target = association.getTargetModel(); const Target = association.getTargetModel();
// 当前表关联 target 表的外键(大部分情况与 target 表主键相同,但可以设置为不同的,要考虑) // 当前表关联 target 表的外键(大部分情况与 target 表主键相同,但可以设置为不同的,要考虑)
const { targetKey = Target.primaryKeyAttribute } = association.options; const { targetKey = Target.primaryKeyAttribute } = association.options;
@ -481,7 +493,7 @@ export abstract class Model extends SequelizeModel {
} }
} }
async updateAssociation(key: string, data: any, options: SaveOptions<any> & { context?: any; }) { async updateAssociation(key: string, data: any, options: UpdateAssociationOptions = {}) {
const table = this.database.getTable(this.constructor.name); const table = this.database.getTable(this.constructor.name);
const association = table.getAssociations().get(key); const association = table.getAssociations().get(key);
switch (true) { switch (true) {
@ -497,15 +509,14 @@ export abstract class Model extends SequelizeModel {
/** /**
* *
* *
* TODO: 暂不支持除主键以外关联字段的更新
*
* @param data * @param data
*/ */
async updateAssociations(data: any, options: SaveOptions & { context?: any } = {}) { async updateAssociations(data: any, options: UpdateAssociationOptions = {}) {
const { transaction = await this.sequelize.transaction() } = options; const { transaction = await this.sequelize.transaction() } = options;
const table = this.database.getTable(this.constructor.name); const table = this.database.getTable(this.constructor.name);
for (const key of table.getAssociations().keys()) { for (const key of table.getAssociations().keys()) {
if (!data[key]) { // 如果 key 不存在才跳过
if (!Object.keys(data).includes(key)) {
continue; continue;
} }
await this.updateAssociation(key, data[key], { await this.updateAssociation(key, data[key], {