mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-02 03:02:19 +08:00
fix: load view collection belongs to association with source options (#3912)
* chore: view collection belongs to source field test * chore: test * fix: load field with source attribute * chore: test
This commit is contained in:
parent
e7187e536d
commit
4ac2875d51
@ -1,10 +1,7 @@
|
|||||||
import { Collection } from '../../collection';
|
|
||||||
import Database from '../../database';
|
import Database from '../../database';
|
||||||
import { InheritedCollection } from '../../inherited-collection';
|
|
||||||
import { mockDatabase } from '../index';
|
import { mockDatabase } from '../index';
|
||||||
import pgOnly from './helper';
|
|
||||||
|
|
||||||
pgOnly()('sync inherits', () => {
|
describe.runIf(process.env['DB_DIALECT'] === 'postgres')('sync inherits', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
const pgOnly = () => (process.env.DB_DIALECT == 'postgres' ? describe : describe.skip);
|
|
||||||
|
|
||||||
export default pgOnly;
|
|
@ -2,9 +2,8 @@ import { vi } from 'vitest';
|
|||||||
import { uid } from '@nocobase/utils';
|
import { uid } from '@nocobase/utils';
|
||||||
import { Database, mockDatabase } from '../../index';
|
import { Database, mockDatabase } from '../../index';
|
||||||
import { ViewCollection } from '../../view-collection';
|
import { ViewCollection } from '../../view-collection';
|
||||||
import pgOnly from '../inhertits/helper';
|
|
||||||
|
|
||||||
pgOnly()('', () => {
|
describe.runIf(process.env['DB_DIALECT'] === 'postgres')('pg only view', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -454,4 +453,90 @@ describe('create view', () => {
|
|||||||
const viewNameField = ViewCollection.getField('name');
|
const viewNameField = ViewCollection.getField('name');
|
||||||
expect(viewNameField.options.patterns).toEqual(UserCollection.getField('name').options.patterns);
|
expect(viewNameField.options.patterns).toEqual(UserCollection.getField('name').options.patterns);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set belongs to field via source', async () => {
|
||||||
|
const User = db.collection({
|
||||||
|
name: 'users',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'posts',
|
||||||
|
target: 'posts',
|
||||||
|
foreignKey: 'userId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Post = db.collection({
|
||||||
|
name: 'posts',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'user',
|
||||||
|
foreignKey: 'userId',
|
||||||
|
target: 'users',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync();
|
||||||
|
|
||||||
|
await User.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 'foo',
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
title: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'baz',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewName = 'posts_view';
|
||||||
|
|
||||||
|
const dropViewSQL = `DROP VIEW IF EXISTS ${viewName}`;
|
||||||
|
await db.sequelize.query(dropViewSQL);
|
||||||
|
|
||||||
|
const viewSQL = `
|
||||||
|
CREATE VIEW ${viewName} as SELECT users.* FROM ${Post.quotedTableName()} as users
|
||||||
|
`;
|
||||||
|
|
||||||
|
await db.sequelize.query(viewSQL);
|
||||||
|
|
||||||
|
// create view collection
|
||||||
|
const ViewCollection = db.collection({
|
||||||
|
name: viewName,
|
||||||
|
view: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
source: 'posts.name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
type: 'belongsTo',
|
||||||
|
source: 'posts.user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
schema: db.inDialect('postgres') ? 'public' : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const post = await ViewCollection.repository.findOne({
|
||||||
|
appends: ['user'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(post['user']['name']).toBe('foo');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -325,13 +325,15 @@ export class Collection<
|
|||||||
this.db.logger.warn(
|
this.db.logger.warn(
|
||||||
`source collection "${sourceCollectionName}" not found for field "${name}" at collection "${this.name}"`,
|
`source collection "${sourceCollectionName}" not found for field "${name}" at collection "${this.name}"`,
|
||||||
);
|
);
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
const sourceField = sourceCollection.fields.get(sourceFieldName);
|
const sourceField = sourceCollection.fields.get(sourceFieldName);
|
||||||
|
|
||||||
if (!sourceField) {
|
if (!sourceField) {
|
||||||
this.db.logger.warn(
|
this.db.logger.warn(
|
||||||
`source field "${sourceFieldName}" not found for field "${name}" at collection "${this.name}"`,
|
`Source field "${sourceFieldName}" not found for field "${name}" at collection "${this.name}". Source collection: "${sourceCollectionName}"`,
|
||||||
);
|
);
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
options = { ...lodash.omit(sourceField.options, ['name', 'primaryKey']), ...options };
|
options = { ...lodash.omit(sourceField.options, ['name', 'primaryKey']), ...options };
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,101 @@ describe('view collection', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should load view collection belongs to field', async () => {
|
||||||
|
await collectionRepository.create({
|
||||||
|
values: {
|
||||||
|
name: 'users',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'posts',
|
||||||
|
target: 'posts',
|
||||||
|
foreignKey: 'userId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await collectionRepository.create({
|
||||||
|
values: {
|
||||||
|
name: 'posts',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'user',
|
||||||
|
foreignKey: 'userId',
|
||||||
|
target: 'users',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.getRepository('users').create({
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: 'u1',
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
title: 'p1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Post = db.getCollection('posts');
|
||||||
|
|
||||||
|
const viewName = `test_view_${uid(6)}`;
|
||||||
|
await db.sequelize.query(`DROP VIEW IF EXISTS ${viewName}`);
|
||||||
|
|
||||||
|
const viewSQL = `
|
||||||
|
CREATE VIEW ${viewName} as SELECT users.* FROM ${Post.quotedTableName()} as users
|
||||||
|
`;
|
||||||
|
|
||||||
|
await db.sequelize.query(viewSQL);
|
||||||
|
|
||||||
|
await collectionRepository.create({
|
||||||
|
values: {
|
||||||
|
name: viewName,
|
||||||
|
view: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
source: 'posts.title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
type: 'belongsTo',
|
||||||
|
source: 'posts.user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
schema: db.inDialect('postgres') ? 'public' : undefined,
|
||||||
|
},
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// recall loadFields
|
||||||
|
await app.runCommand('restart');
|
||||||
|
|
||||||
|
db = app.db;
|
||||||
|
|
||||||
|
const viewCollection = db.getCollection(viewName);
|
||||||
|
await viewCollection.repository.find({
|
||||||
|
appends: ['user'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should use view collection as through collection', async () => {
|
it('should use view collection as through collection', async () => {
|
||||||
const User = await collectionRepository.create({
|
const User = await collectionRepository.create({
|
||||||
values: {
|
values: {
|
||||||
@ -168,8 +263,6 @@ describe('view collection', function () {
|
|||||||
|
|
||||||
const UserCollection = db.getCollection('users');
|
const UserCollection = db.getCollection('users');
|
||||||
|
|
||||||
console.log(UserCollection);
|
|
||||||
|
|
||||||
await db.getRepository('users').create({
|
await db.getRepository('users').create({
|
||||||
values: [{ name: 'u1' }, { name: 'u2' }],
|
values: [{ name: 'u1' }, { name: 'u2' }],
|
||||||
});
|
});
|
||||||
|
@ -91,11 +91,14 @@ export class CollectionRepository extends Repository {
|
|||||||
submodule: 'CollectionRepository',
|
submodule: 'CollectionRepository',
|
||||||
method: 'load',
|
method: 'load',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.setMaintainingMessage(`load ${instanceName} collection`);
|
this.app.setMaintainingMessage(`load ${instanceName} collection`);
|
||||||
|
|
||||||
await nameMap[instanceName].load({ skipField });
|
await nameMap[instanceName].load({ skipField });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fieldWithSourceAttributes = new Map<string, Array<string>>();
|
||||||
|
|
||||||
// load view fields
|
// load view fields
|
||||||
for (const viewCollectionName of viewCollections) {
|
for (const viewCollectionName of viewCollections) {
|
||||||
this.database.logger.debug(`load collection fields`, {
|
this.database.logger.debug(`load collection fields`, {
|
||||||
@ -103,8 +106,20 @@ export class CollectionRepository extends Repository {
|
|||||||
method: 'load',
|
method: 'load',
|
||||||
viewCollectionName,
|
viewCollectionName,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const skipField = (() => {
|
||||||
|
const fields = nameMap[viewCollectionName].get('fields');
|
||||||
|
|
||||||
|
return fields.filter((field) => field.options?.source).map((field) => field.get('name'));
|
||||||
|
})();
|
||||||
|
|
||||||
this.app.setMaintainingMessage(`load ${viewCollectionName} collection fields`);
|
this.app.setMaintainingMessage(`load ${viewCollectionName} collection fields`);
|
||||||
await nameMap[viewCollectionName].loadFields({});
|
|
||||||
|
if (lodash.isArray(skipField) && skipField.length) {
|
||||||
|
fieldWithSourceAttributes.set(viewCollectionName, skipField);
|
||||||
|
}
|
||||||
|
|
||||||
|
await nameMap[viewCollectionName].loadFields({ skipField });
|
||||||
}
|
}
|
||||||
|
|
||||||
// load lazy collection field
|
// load lazy collection field
|
||||||
@ -117,6 +132,18 @@ export class CollectionRepository extends Repository {
|
|||||||
this.app.setMaintainingMessage(`load ${collectionName} collection fields`);
|
this.app.setMaintainingMessage(`load ${collectionName} collection fields`);
|
||||||
await nameMap[collectionName].loadFields({ includeFields: skipField });
|
await nameMap[collectionName].loadFields({ includeFields: skipField });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load source attribute fields
|
||||||
|
for (const [collectionName, skipField] of fieldWithSourceAttributes) {
|
||||||
|
this.database.logger.debug(`load collection fields`, {
|
||||||
|
submodule: 'CollectionRepository',
|
||||||
|
method: 'load',
|
||||||
|
collectionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.app.setMaintainingMessage(`load ${collectionName} collection fields`);
|
||||||
|
await nameMap[collectionName].loadFields({ includeFields: skipField });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async db2cm(collectionName: string) {
|
async db2cm(collectionName: string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user