Fix for many to many relationships where the union table has arbituarily named foreign key constraint columns, an example has been provided in the scripts directory.

This commit is contained in:
mike12345567 2021-10-01 17:16:43 +01:00
parent a8806244dc
commit a343805fee
8 changed files with 107 additions and 9 deletions

View File

@ -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})`}

View File

@ -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:

View File

@ -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');

View File

@ -0,0 +1,3 @@
#!/bin/bash
docker-compose down
docker volume prune -f

View File

@ -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: {

View File

@ -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

View File

@ -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
} }

View File

@ -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