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:
parent
a1ee2afabd
commit
1defb5db51
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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({
|
||||
|
@ -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');
|
||||
|
Loading…
Reference in New Issue
Block a user