Merge pull request #2857 from Budibase/fix/2792
Allow SQL many to many relationships to use arbitrarily named columns in junction table
This commit is contained in:
commit
553bd63842
|
@ -156,6 +156,8 @@
|
||||||
...relateTo,
|
...relateTo,
|
||||||
through: through._id,
|
through: through._id,
|
||||||
fieldName: fromTable.primary[0],
|
fieldName: fromTable.primary[0],
|
||||||
|
throughFrom: relateFrom.throughTo,
|
||||||
|
throughTo: relateFrom.throughFrom,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the relateFrom.fieldName should remain the same, as it is the foreignKey in the other
|
// the relateFrom.fieldName should remain the same, as it is the foreignKey in the other
|
||||||
|
@ -251,6 +253,22 @@
|
||||||
bind:error={errors.through}
|
bind:error={errors.through}
|
||||||
bind:value={fromRelationship.through}
|
bind:value={fromRelationship.through}
|
||||||
/>
|
/>
|
||||||
|
{#if fromTable && toTable && through}
|
||||||
|
<Select
|
||||||
|
label={`Foreign Key (${fromTable?.name})`}
|
||||||
|
options={Object.keys(through?.schema)}
|
||||||
|
on:change={() => ($touched.fromForeign = true)}
|
||||||
|
bind:error={errors.fromForeign}
|
||||||
|
bind:value={fromRelationship.throughTo}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
label={`Foreign Key (${toTable?.name})`}
|
||||||
|
options={Object.keys(through?.schema)}
|
||||||
|
on:change={() => ($touched.toForeign = true)}
|
||||||
|
bind:error={errors.toForeign}
|
||||||
|
bind:value={fromRelationship.throughFrom}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{:else if fromRelationship?.relationshipType && toTable}
|
{:else if fromRelationship?.relationshipType && toTable}
|
||||||
<Select
|
<Select
|
||||||
label={`Foreign Key (${toTable?.name})`}
|
label={`Foreign Key (${toTable?.name})`}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
container_name: postgres
|
||||||
|
image: postgres
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: root
|
||||||
|
POSTGRES_PASSWORD: root
|
||||||
|
POSTGRES_DB: main
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
#- pg_data:/var/lib/postgresql/data/
|
||||||
|
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
|
|
||||||
|
pgadmin:
|
||||||
|
container_name: pgadmin-pg
|
||||||
|
image: dpage/pgadmin4
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: root@root.com
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: root
|
||||||
|
ports:
|
||||||
|
- "5050:80"
|
||||||
|
|
||||||
|
#volumes:
|
||||||
|
# pg_data:
|
|
@ -0,0 +1,41 @@
|
||||||
|
SELECT 'CREATE DATABASE main'
|
||||||
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'main')\gexec
|
||||||
|
|
||||||
|
CREATE TABLE categories
|
||||||
|
(
|
||||||
|
name text COLLATE pg_catalog."default",
|
||||||
|
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||||
|
CONSTRAINT categories_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE customers
|
||||||
|
(
|
||||||
|
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||||
|
name text COLLATE pg_catalog."default",
|
||||||
|
email text COLLATE pg_catalog."default",
|
||||||
|
age integer,
|
||||||
|
"dateOfBirth" date,
|
||||||
|
CONSTRAINT customers_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE customer_category
|
||||||
|
(
|
||||||
|
customer_id integer,
|
||||||
|
category_id integer,
|
||||||
|
notes text COLLATE pg_catalog."default",
|
||||||
|
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
|
||||||
|
CONSTRAINT "Category" FOREIGN KEY (category_id)
|
||||||
|
REFERENCES public.categories (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION
|
||||||
|
NOT VALID,
|
||||||
|
CONSTRAINT "Customer" FOREIGN KEY (customer_id)
|
||||||
|
REFERENCES public.customers (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION
|
||||||
|
NOT VALID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO customers (name, email, age) VALUES ('Mike', 'mike@mike.com', 30);
|
||||||
|
INSERT INTO categories (name) VALUES ('Books');
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
docker-compose down
|
||||||
|
docker volume prune -f
|
|
@ -205,9 +205,9 @@ module External {
|
||||||
} else {
|
} else {
|
||||||
// we're not inserting a doc, will be a bunch of update calls
|
// we're not inserting a doc, will be a bunch of update calls
|
||||||
const isUpdate = !field.through
|
const isUpdate = !field.through
|
||||||
const thisKey: string = isUpdate ? "id" : linkTablePrimary
|
const thisKey: string = isUpdate ? "id" : (field.throughTo || linkTablePrimary)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const otherKey: string = isUpdate ? field.fieldName : tablePrimary
|
const otherKey: string = isUpdate ? field.fieldName : (field.throughFrom || tablePrimary)
|
||||||
row[key].map((relationship: any) => {
|
row[key].map((relationship: any) => {
|
||||||
// we don't really support composite keys for relationships, this is why [0] is used
|
// we don't really support composite keys for relationships, this is why [0] is used
|
||||||
manyRelationships.push({
|
manyRelationships.push({
|
||||||
|
@ -328,12 +328,11 @@ module External {
|
||||||
if (!table.primary || !linkTable.primary) {
|
if (!table.primary || !linkTable.primary) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const definition = {
|
const definition: any = {
|
||||||
// if no foreign key specified then use the name of the field in other table
|
// if no foreign key specified then use the name of the field in other table
|
||||||
from: field.foreignKey || table.primary[0],
|
from: field.foreignKey || table.primary[0],
|
||||||
to: field.fieldName,
|
to: field.fieldName,
|
||||||
tableName: linkTableName,
|
tableName: linkTableName,
|
||||||
through: undefined,
|
|
||||||
// need to specify where to put this back into
|
// need to specify where to put this back into
|
||||||
column: fieldName,
|
column: fieldName,
|
||||||
}
|
}
|
||||||
|
@ -343,8 +342,10 @@ module External {
|
||||||
)
|
)
|
||||||
definition.through = throughTableName
|
definition.through = throughTableName
|
||||||
// don't support composite keys for relationships
|
// don't support composite keys for relationships
|
||||||
definition.from = table.primary[0]
|
definition.from = field.throughFrom || table.primary[0]
|
||||||
definition.to = linkTable.primary[0]
|
definition.to = field.throughTo || linkTable.primary[0]
|
||||||
|
definition.fromPrimary = table.primary[0]
|
||||||
|
definition.toPrimary = linkTable.primary[0]
|
||||||
}
|
}
|
||||||
relationships.push(definition)
|
relationships.push(definition)
|
||||||
}
|
}
|
||||||
|
@ -369,7 +370,8 @@ module External {
|
||||||
}
|
}
|
||||||
const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY
|
const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY
|
||||||
const tableId = isMany ? field.through : field.tableId
|
const tableId = isMany ? field.through : field.tableId
|
||||||
const fieldName = isMany ? primaryKey : field.fieldName
|
const manyKey = field.throughFrom || primaryKey
|
||||||
|
const fieldName = isMany ? manyKey : field.fieldName
|
||||||
const response = await makeExternalQuery(this.appId, {
|
const response = await makeExternalQuery(this.appId, {
|
||||||
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
||||||
filters: {
|
filters: {
|
||||||
|
|
|
@ -15,6 +15,8 @@ export interface FieldSchema {
|
||||||
through?: string
|
through?: string
|
||||||
foreignKey?: string
|
foreignKey?: string
|
||||||
autocolumn?: boolean
|
autocolumn?: boolean
|
||||||
|
throughFrom?: string
|
||||||
|
throughTo?: string
|
||||||
constraints?: {
|
constraints?: {
|
||||||
type?: string
|
type?: string
|
||||||
email?: boolean
|
email?: boolean
|
||||||
|
|
|
@ -121,6 +121,8 @@ export interface RelationshipsJson {
|
||||||
through?: string
|
through?: string
|
||||||
from?: string
|
from?: string
|
||||||
to?: string
|
to?: string
|
||||||
|
fromPrimary?: string
|
||||||
|
toPrimary?: string
|
||||||
tableName: string
|
tableName: string
|
||||||
column: string
|
column: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,14 +112,16 @@ function addRelationships(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const throughTable = relationship.through
|
const throughTable = relationship.through
|
||||||
|
const fromPrimary = relationship.fromPrimary
|
||||||
|
const toPrimary = relationship.toPrimary
|
||||||
query = query
|
query = query
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
throughTable,
|
throughTable,
|
||||||
`${fromTable}.${from}`,
|
`${fromTable}.${fromPrimary}`,
|
||||||
`${throughTable}.${from}`
|
`${throughTable}.${from}`
|
||||||
)
|
)
|
||||||
.leftJoin(toTable, `${toTable}.${to}`, `${throughTable}.${to}`)
|
.leftJoin(toTable, `${toTable}.${toPrimary}`, `${throughTable}.${to}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
|
|
Loading…
Reference in New Issue