diff --git a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
index e17c949c7a..33e8710e88 100644
--- a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
+++ b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
@@ -41,23 +41,26 @@
-
{{ bt._rtn }} (Belongs To)
|
-
{{ hm._tn }} (Has Many)
|
-
{{ mm.rtn }} (Many To Many)
|
@@ -287,17 +290,32 @@
-
- {{ Object.values(rowObj[bt._rtn])[1] }}
+ |
+ {{
+ Object.values(rowObj[bt._rtn])[1]
+ }}
+
|
-
-
- {{ v }}
-
+ |
+
+
+
+
+
+
+
|
-
- {{
+
+ {{
v
}}
@@ -331,9 +349,10 @@ import EditColumn from "@/components/project/spreadsheet/editColumn/editColumn";
import TableCell from "@/components/project/spreadsheet/editableCell/tableCell";
import colors from "@/mixins/colors";
import columnStyling from "@/components/project/spreadsheet/helpers/columnStyling";
+import HasManyCell from "@/components/project/spreadsheet/editableCell/hasManyCell";
export default {
- components: {TableCell, EditColumn, EditableCell, HeaderCell},
+ components: {HasManyCell, TableCell, EditColumn, EditableCell, HeaderCell},
mixins: [colors],
props: {
relationType: String,
@@ -842,8 +861,8 @@ th:first-child, td:first-child {
transform: rotate(90deg);
}
-th{
- min-width:100px;
+th {
+ min-width: 100px;
}
diff --git a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
index ee779d9fb4..28376ddbbb 100644
--- a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
+++ b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
@@ -1499,15 +1499,20 @@ export default abstract class BaseApiBuilder implements XcDynami
protected async getManyToManyRelations() {
const metas = new Set();
+ const assocMetas = new Set();
for (const meta of Object.values(this.metas)) {
// check if table is a Bridge table(or Associative Table) by checking
// number of foreign keys and columns
- if (meta.belongsTo?.length === 2 && meta.columns.length < 4) {
+ if (meta.belongsTo?.length === 2 && meta.columns.length < 5) {
const tableMetaA = this.metas[meta.belongsTo[0].rtn];
const tableMetaB = this.metas[meta.belongsTo[1].rtn];
+/* // remove hasmany relation with associate table from tables
+ tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1)
+ tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/
+
// add manytomany data under metadata of both related columns
tableMetaA.manyToMany = tableMetaA.manyToMany || [];
tableMetaA.manyToMany.push({
@@ -1539,6 +1544,7 @@ export default abstract class BaseApiBuilder implements XcDynami
})
metas.add(tableMetaA)
metas.add(tableMetaB)
+ assocMetas.add(meta)
}
}
@@ -1551,6 +1557,15 @@ export default abstract class BaseApiBuilder implements XcDynami
XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::'));
this.models[meta.tn] = this.getBaseModel(meta)
}
+
+ // Update metadata of associative table
+ for (const meta of assocMetas) {
+ await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
+ mm: 1,
+ }, {title: meta.tn})
+ XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::'));
+ this.models[meta.tn] = this.getBaseModel(meta)
+ }
}
}
diff --git a/packages/nocodb/src/lib/noco/common/XcMigrationSource.ts b/packages/nocodb/src/lib/noco/common/XcMigrationSource.ts
index f2f5e49ab3..4f77f87673 100644
--- a/packages/nocodb/src/lib/noco/common/XcMigrationSource.ts
+++ b/packages/nocodb/src/lib/noco/common/XcMigrationSource.ts
@@ -1,4 +1,5 @@
import * as project from '../migrations/nc_001_init';
+import * as m2m from '../migrations/nc_002_add_m2m';
// Create a custom migration source class
export default class XcMigrationSource{
@@ -7,7 +8,7 @@ export default class XcMigrationSource{
// arguments to getMigrationName and getMigration
public getMigrations(): Promise {
// In this example we are just returning migration names
- return Promise.resolve(['project'])
+ return Promise.resolve(['project','m2m'])
}
public getMigrationName(migration): string {
@@ -18,6 +19,8 @@ export default class XcMigrationSource{
switch (migration) {
case 'project':
return project;
+ case 'm2m':
+ return m2m;
}
}
}
diff --git a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
index ba27738b29..1b305176cc 100644
--- a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
+++ b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
@@ -643,55 +643,16 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr {
/* has many relation list loader with middleware */
const mw = new GqlMiddleware(this.acls, hm.tn, '', this.models);
- const listLoader = new DataLoader(
- BaseType.applyMiddlewareForLoader(
- [mw.middleware],
- async ids => {
- const data = await this.models[tn].hasManyListGQL({
- ids,
- child: hm.tn
- })
- return ids.map(id => data[id] ? data[id].map(c => new self.types[hm.tn](c)) : []);
- },
- [mw.postLoaderMiddleware]
- ));
-
- /* defining HasMany list method within GQL Type class */
- Object.defineProperty(this.types[tn].prototype, `${listPropName}`, {
- async value(args, context, info): Promise {
- return listLoader.load([this[colNameAlias], args, context, info]);
- },
- configurable: true
- })
-
-
+ /* has many relation list loader with middleware */
+ this.addHmListResolverMethodToType(tn, hm, mw, {}, listPropName, colNameAlias);
if (countPropName in this.types[tn].prototype) {
continue;
}
-
- // create count loader with middleware
{
- const mw = new GqlMiddleware(this.acls, hm.tn, '', this.models);
- const countLoader = new DataLoader(
- BaseType.applyMiddlewareForLoader(
- [mw.middleware],
- async ids => {
- const data = await this.models[tn].hasManyListCount({
- ids,
- child: hm.tn
- })
- return data;
- },
- [mw.postLoaderMiddleware]
- ));
-
- // defining HasMany count method within GQL Type class
- Object.defineProperty(this.types[tn].prototype, `${countPropName}`, {
- async value(args, context, info): Promise {
- return countLoader.load([this[colNameAlias], args, context, info]);
- },
- configurable: true
- })
+ const mw = new GqlMiddleware(this.acls, hm.tn, null, this.models);
+
+ // create count loader with middleware
+ this.addHmCountResolverMethodToType(hm, mw, tn, {}, countPropName, colNameAlias);
}
this.log(`xcTablesPopulate : Inserting loader metadata of '%s' and '%s' loaders`, listPropName, countPropName);
@@ -716,33 +677,21 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr {
for (const bt of schema.belongsTo) {
const colNameAlias = self.models[bt.tn]?.columnToAlias[bt.cn];
const propName = `${inflection.camelize(bt._rtn, false)}Read`;
+
+
if (propName in this.types[tn].prototype) {
continue;
}
- this.log(`xcTablesPopulate : Populating '%s' loader`, propName);
+
// create read loader with middleware
- const mw = new GqlMiddleware(this.acls, bt.rtn, '', this.models);
- const readLoader = new DataLoader(
- BaseType.applyMiddlewareForLoader(
- [mw.middleware],
- async ids => {
- const data = await self.models[bt.rtn].list({
- where: `(${bt.rcn},in,${ids.join(',')})`,
- limit: ids.length
- })
- const gs = _.groupBy(data, bt.rcn);
- return ids.map(async id => gs?.[id]?.[0] && new self.types[bt.rtn](gs[id][0]))
- },
- [mw.postLoaderMiddleware]
- ));
-
- // defining BelongsTo read method within GQL Type class
- Object.defineProperty(this.types[tn].prototype, `${propName}`, {
- async value(args, context, info): Promise {
- return readLoader.load([this[colNameAlias], args, context, info]);
- },
- configurable: true
- });
+ {
+ const mw = new GqlMiddleware(this.acls, bt.rtn, null, this.models);
+ this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Bt${bt.rtn}`);
+ this.adBtResolverMethodToType(propName, mw,
+ tn, bt, colNameAlias, colNameAlias, null);
+ }
+
+
this.log(`xcTablesPopulate : Inserting loader metadata of '%s' loader`, propName);
await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', {
diff --git a/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts b/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts
new file mode 100644
index 0000000000..60481ad1a5
--- /dev/null
+++ b/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts
@@ -0,0 +1,43 @@
+import Knex from "knex";
+
+const up = async (knex: Knex) => {
+ await knex.schema.alterTable('nc_models', table => {
+ table.integer('mm');
+ table.text('m_to_m_meta');
+ })
+};
+
+const down = async (knex) => {
+ await knex.schema.alterTable('nc_models', table => {
+ table.dropColumns('mm', 'm_to_m_meta');
+ })
+};
+
+
+export {
+ up, down
+}
+
+
+ /**
+ * @copyright Copyright (c) 2021, Xgene Cloud Ltd
+ *
+ * @author Naveen MR
+ * @author Pranav C Balan
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
\ No newline at end of file
| |