fix: create collections with multiple records (#2753)

* fix: create collections with multiple items

* chore: test

* fix: sync collection when pending field resolved

* fix: test
This commit is contained in:
ChengLei Shao 2023-10-09 17:35:04 +08:00 committed by GitHub
parent a1ee2afabd
commit 1defb5db51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 185 additions and 3 deletions

View File

@ -555,7 +555,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
const instances = [];
for (const values of records) {
const instance = await this.create({ values, transaction });
const instance = await this.create({ ...options, values, transaction });
instances.push(instance);
}

View File

@ -20,6 +20,165 @@ describe('collections repository', () => {
await app.destroy();
});
it('should create through table when pending fields', async () => {
await Collection.repository.create({
values: {
name: 'posts',
fields: [
{
type: 'string',
name: 'title',
},
{
type: 'belongsToMany',
target: 'tags',
name: 'tags',
through: 'posts_tags',
foreignKey: 'post_id',
otherKey: 'tag_id',
sourceKey: 'id',
targetKey: 'id',
},
],
},
context: {},
});
const postsCollection = db.getCollection('posts');
expect(postsCollection).toBeTruthy();
await Collection.repository.create({
values: {
name: 'tags',
fields: [
{
type: 'string',
name: 'name',
},
],
},
context: {},
});
const throughCollection = db.getCollection('posts_tags');
expect(throughCollection).toBeTruthy();
expect(await throughCollection.existsInDb()).toBeTruthy();
const rawAttribute = throughCollection.model.rawAttributes['tag_id'].field;
const columns = await db.sequelize.getQueryInterface().describeTable(throughCollection.getTableNameWithSchema());
expect(columns[rawAttribute]).toBeDefined();
});
it('should create collections with array', async () => {
await Collection.repository.create({
values: [
{
name: 'users',
fields: [
{
type: 'string',
name: 'username',
},
{
type: 'hasMany',
target: 'posts',
name: 'posts',
},
{
type: 'hasOne',
target: 'profiles',
name: 'profile',
},
],
},
{
name: 'profiles',
fields: [
{
type: 'string',
name: 'nickname',
},
{
type: 'belongsTo',
target: 'users',
name: 'user',
},
],
},
{
name: 'posts',
fields: [
{
type: 'string',
name: 'title',
},
{
type: 'belongsToMany',
target: 'tags',
name: 'tags',
through: 'posts_tags',
foreignKey: 'post_id',
otherKey: 'tag_id',
sourceKey: 'id',
targetKey: 'id',
},
],
},
{
name: 'tags',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'belongsToMany',
target: 'posts',
name: 'posts',
through: 'posts_tags',
foreignKey: 'tag_id',
otherKey: 'post_id',
sourceKey: 'id',
targetKey: 'id',
},
],
},
],
context: {},
});
const postsCollection = db.getCollection('posts');
expect(postsCollection).toBeTruthy();
const tagsCollection = db.getCollection('tags');
expect(tagsCollection).toBeTruthy();
const usersCollection = db.getCollection('users');
expect(usersCollection).toBeTruthy();
const profilesCollection = db.getCollection('profiles');
expect(profilesCollection).toBeTruthy();
await usersCollection.repository.create({
values: {
username: 'admin',
profile: {
nickname: '管理员',
},
posts: [
{
title: 'test',
tags: [
{
name: 'test',
},
],
},
],
},
});
});
it('should create collection with description', async () => {
const description = 'this collection is for tests';
await Collection.repository.create({

View File

@ -1,6 +1,7 @@
import Database, { Collection, MagicAttributeModel, SyncOptions, Transactionable } from '@nocobase/database';
import lodash from 'lodash';
import { FieldModel } from './field';
import { async } from 'fast-glob';
interface LoadOptions extends Transactionable {
// TODO
@ -114,10 +115,26 @@ export class CollectionModel extends MagicAttributeModel {
}
async migrate(options?: SyncOptions & Transactionable) {
const pendingFieldsTargetToThis = this.db.pendingFields.get(this.get('name')) || [];
const getPendingField = () =>
pendingFieldsTargetToThis.map((field) => {
return {
name: field.get('name'),
collectionName: field.get('collectionName'),
};
});
const beforePendingFields = getPendingField();
const collection = await this.load({
transaction: options?.transaction,
});
const afterPendingFields = getPendingField();
const resolvedPendingFields = lodash.differenceWith(beforePendingFields, afterPendingFields, lodash.isEqual);
const resolvedPendingFieldsCollections = lodash.uniq(resolvedPendingFields.map((field) => field.collectionName));
// postgres support zero column table, other database should not sync it to database
// @ts-ignore
if (Object.keys(collection.model.tableAttributes).length == 0 && !this.db.inDialect('postgres')) {
@ -125,13 +142,19 @@ export class CollectionModel extends MagicAttributeModel {
}
try {
await collection.sync({
const syncOptions = {
force: false,
alter: {
drop: false,
},
...options,
});
};
await collection.sync(syncOptions);
for (const collectionName of resolvedPendingFieldsCollections) {
await this.db.getCollection(collectionName).sync(syncOptions);
}
} catch (error) {
console.error(error);
const name = this.get('name');