mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-11-29 18:58:26 +08:00
fix: through table primaryKey error (#297)
* fix: through table primaryKey error * chore: sort fields * chore: test release * chore: github action sqlite env
This commit is contained in:
parent
28f9b902e8
commit
a37609e71b
2
.github/workflows/node-ci.yml
vendored
2
.github/workflows/node-ci.yml
vendored
@ -55,7 +55,7 @@ jobs:
|
||||
run: yarn test -i
|
||||
env:
|
||||
DB_DIALECT: sqlite
|
||||
DB_STORAGE: ":memory:"
|
||||
DB_STORAGE: /tmp/db.sqlite
|
||||
- name: Test with MySQL
|
||||
run: yarn test -i
|
||||
env:
|
||||
|
@ -11,7 +11,6 @@ export type RepositoryType = typeof Repository;
|
||||
|
||||
export type CollectionSortable = string | boolean | { name?: string; scopeKey?: string };
|
||||
|
||||
|
||||
export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
|
||||
name: string;
|
||||
tableName?: string;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
// @ts-ignore
|
||||
import { pathToRegexp } from 'path-to-regexp';
|
||||
import qs from 'qs';
|
||||
import { ResourceType } from './resource';
|
||||
|
@ -2,12 +2,18 @@ import { mockServer } from '@nocobase/test';
|
||||
import PluginUiSchema from '@nocobase/plugin-ui-schema-storage';
|
||||
|
||||
import CollectionManagerPlugin from '..';
|
||||
import lodash from 'lodash';
|
||||
|
||||
export async function createApp() {
|
||||
export async function createApp(options = {}) {
|
||||
const app = mockServer();
|
||||
await app.cleanDb();
|
||||
|
||||
if (lodash.get(options, 'cleanDB', true)) {
|
||||
await app.cleanDb();
|
||||
}
|
||||
|
||||
app.plugin(CollectionManagerPlugin);
|
||||
app.plugin(PluginUiSchema);
|
||||
|
||||
await app.load();
|
||||
return app;
|
||||
}
|
||||
|
@ -0,0 +1,148 @@
|
||||
import { mockServer, MockServer } from '@nocobase/test';
|
||||
import CollectionManagerPlugin from '../plugin';
|
||||
|
||||
describe('collections repository', () => {
|
||||
let app: MockServer;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = mockServer({
|
||||
database: {
|
||||
tablePrefix: 'through_',
|
||||
},
|
||||
});
|
||||
await app.cleanDb();
|
||||
app.plugin(CollectionManagerPlugin);
|
||||
await app.load();
|
||||
await app.install({ clean: true });
|
||||
await app.start();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.destroy();
|
||||
});
|
||||
|
||||
it('case 1', async () => {
|
||||
await app
|
||||
.agent()
|
||||
.resource('collections')
|
||||
.create({
|
||||
values: {
|
||||
name: 'resumes',
|
||||
createdBy: true,
|
||||
updatedBy: true,
|
||||
sortable: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'integer',
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||
interface: 'id',
|
||||
},
|
||||
],
|
||||
title: '简历',
|
||||
},
|
||||
});
|
||||
|
||||
await app
|
||||
.agent()
|
||||
.resource('collections')
|
||||
.create({
|
||||
values: {
|
||||
name: 'jobs',
|
||||
createdBy: true,
|
||||
updatedBy: true,
|
||||
sortable: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'integer',
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||
interface: 'id',
|
||||
},
|
||||
],
|
||||
title: '职位',
|
||||
},
|
||||
});
|
||||
|
||||
await app
|
||||
.agent()
|
||||
.resource('collections')
|
||||
.create({
|
||||
values: {
|
||||
name: 'matches',
|
||||
createdBy: true,
|
||||
updatedBy: true,
|
||||
sortable: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'integer',
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||
interface: 'id',
|
||||
},
|
||||
],
|
||||
title: '匹配',
|
||||
},
|
||||
});
|
||||
|
||||
await app
|
||||
.agent()
|
||||
.resource('collections.fields', 'resumes')
|
||||
.create({
|
||||
values: {
|
||||
name: 'jobs',
|
||||
type: 'belongsToMany',
|
||||
uiSchema: {
|
||||
'x-component': 'RecordPicker',
|
||||
'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
|
||||
title: '职位',
|
||||
},
|
||||
reverseField: {
|
||||
interface: 'linkTo',
|
||||
type: 'belongsToMany',
|
||||
uiSchema: {
|
||||
'x-component': 'RecordPicker',
|
||||
'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
|
||||
title: '简历',
|
||||
},
|
||||
},
|
||||
interface: 'linkTo',
|
||||
target: 'jobs',
|
||||
through: 'matches',
|
||||
},
|
||||
});
|
||||
|
||||
const matchesCollection = app.db.getCollection('matches');
|
||||
|
||||
const matchesFields = [...matchesCollection.fields.entries()];
|
||||
const matchJobField = matchesFields.find((item) => item[1].options.target == 'jobs');
|
||||
|
||||
expect(matchesCollection.model.rawAttributes[matchJobField[1].options.foreignKey].primaryKey).not.toBeTruthy();
|
||||
|
||||
const app2 = mockServer({
|
||||
database: {
|
||||
tablePrefix: 'through_',
|
||||
},
|
||||
});
|
||||
app2.plugin(CollectionManagerPlugin);
|
||||
await app2.load();
|
||||
await app2.start();
|
||||
|
||||
await app2.db.sync();
|
||||
|
||||
expect(
|
||||
app.db.getCollection('matches').model.rawAttributes[matchJobField[1].options.foreignKey].primaryKey,
|
||||
).not.toBeTruthy();
|
||||
|
||||
await app2.destroy();
|
||||
});
|
||||
});
|
@ -44,6 +44,7 @@ export class CollectionModel extends MagicAttributeModel {
|
||||
async loadFields(options: Transactionable = {}) {
|
||||
// @ts-ignore
|
||||
const instances: FieldModel[] = await this.getFields(options);
|
||||
|
||||
for (const instance of instances) {
|
||||
await instance.load(options);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
afterCreateForReverseField,
|
||||
beforeCreateForChildrenCollection,
|
||||
beforeCreateForReverseField,
|
||||
beforeInitOptions
|
||||
beforeInitOptions,
|
||||
} from './hooks';
|
||||
import { CollectionModel, FieldModel } from './models';
|
||||
|
||||
|
@ -10,6 +10,30 @@ export class CollectionRepository extends Repository {
|
||||
async load(options: LoadOptions = {}) {
|
||||
const { filter, skipExist } = options;
|
||||
const instances = (await this.find({ filter })) as CollectionModel[];
|
||||
|
||||
const throughModels = [];
|
||||
|
||||
for (const instance of instances) {
|
||||
// @ts-ignore
|
||||
const fields = await instance.getFields();
|
||||
for (const field of fields) {
|
||||
if (field['type'] === 'belongsToMany') {
|
||||
const throughName = field.options.through;
|
||||
if (throughName) {
|
||||
throughModels.push(throughName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instances.sort((a, b) => {
|
||||
if (throughModels.includes(a.get('name'))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
for (const instance of instances) {
|
||||
await instance.load({ skipExist });
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user