chore: import upstream snapshot with attribution
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Master-Detail Forms (Nested Forms)
|
||||
|
||||
This example shows how to handle inserting data into multiple tables
|
||||
with a one-to-many relationship.
|
||||
|
||||
The example uses a **SQLite** database with the following schema:
|
||||
|
||||
| Database Schema | SQLPage Form |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
A single master page features two forms based on two related tables or views.
|
||||
Users insert data in the master form first to update information from the parent table.
|
||||
Then, they can insert data in the detail forms to update information from the child tables.
|
||||
|
||||
This example application contains a main form to create users,
|
||||
and a second form to create their addresses.
|
||||
|
||||
Once a user has been added, multiple addresses can be added to it.
|
||||
|
||||
See https://github.com/sqlpage/SQLPage/discussions/16 for more details.
|
||||
|
||||
The main idea is to create two separate forms.
|
||||
In this example, we put both forms on the same page, in [`edit-user.sql`](./edit-user.sql).
|
||||
The first one is an edition form for the already-existing user record,
|
||||
and the second is a form to add an address to the user.
|
||||
|
||||
When you initially load the user creation form,
|
||||
we do not display the address form.
|
||||
Only when the user has been created,
|
||||
you are redirected to the user edition form that contains the address form.
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
web:
|
||||
image: lovasoa/sqlpage:main
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- .:/var/www
|
||||
- ./sqlpage:/etc/sqlpage
|
||||
@@ -0,0 +1,25 @@
|
||||
SELECT 'shell' AS component, 'User Management App' AS title, 'user' AS icon, '/' AS link;
|
||||
|
||||
SELECT 'form' AS component,
|
||||
'Edit user' AS title,
|
||||
'insert_user.sql' || COALESCE('?id=' || $id, '') AS action;
|
||||
|
||||
SELECT 'First name' AS name,
|
||||
TRUE AS required,
|
||||
(SELECT first_name FROM "user" WHERE id = CAST($id AS INT)) AS value;
|
||||
|
||||
SELECT 'Last name' AS name,
|
||||
TRUE AS required,
|
||||
(SELECT last_name FROM "user" WHERE id = CAST($id AS INT)) AS value;
|
||||
|
||||
SELECT 'Email' AS name,
|
||||
'email' AS type,
|
||||
(SELECT email FROM "user" WHERE id = CAST($id AS INT)) AS value;
|
||||
|
||||
SELECT 'list' AS component, 'Addresses' AS title WHERE $id IS NOT NULL;
|
||||
SELECT street || ', ' || city || ', ' || country AS title FROM address WHERE user_id = CAST($id AS INT);
|
||||
|
||||
SELECT 'form' AS component, 'Add address' AS title, 'insert_address.sql?user_id=' || $id AS action WHERE $id IS NOT NULL;
|
||||
SELECT 'Street' AS name, TRUE AS required WHERE $id IS NOT NULL;
|
||||
SELECT 'City' AS name, TRUE AS required WHERE $id IS NOT NULL;
|
||||
SELECT 'Country' AS name, TRUE AS required WHERE $id IS NOT NULL;
|
||||
@@ -0,0 +1,10 @@
|
||||
SELECT 'hero' as component,
|
||||
'SQLPage Form Demo' as title,
|
||||
'This application allows you to manage a list of users and their addresses' as description_md,
|
||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Lac_de_Zoug.jpg/640px-Lac_de_Zoug.jpg' as image,
|
||||
'edit_user.sql' as link,
|
||||
'Create a new user' as link_text;
|
||||
|
||||
SELECT 'list' AS component, 'Users' AS title;
|
||||
SELECT first_name || ' ' || last_name AS title, email AS description, 'edit_user.sql?id=' || id AS link FROM "user";
|
||||
SELECT 'Add a new user' AS title, 'edit_user.sql' AS link, TRUE AS active;
|
||||
@@ -0,0 +1,4 @@
|
||||
INSERT INTO address (user_id, street, city, country) VALUES ($user_id, :Street, :City, :Country)
|
||||
RETURNING
|
||||
'redirect' AS component,
|
||||
'edit_user.sql?id=' || user_id AS link;
|
||||
@@ -0,0 +1,6 @@
|
||||
INSERT INTO user (id, first_name, last_name, email) VALUES (CAST($id AS INT), :"First name", :"Last name", :Email)
|
||||
ON CONFLICT (id) DO -- this syntax is PostgreSQL-specific. In SQLite, use ON CONFLICT IGNORE.
|
||||
UPDATE SET first_name = excluded.first_name, last_name = excluded.last_name, email = excluded.email
|
||||
RETURNING
|
||||
'redirect' AS component,
|
||||
'edit_user.sql?id=' || id AS link;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 430 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE "user"(
|
||||
id INTEGER PRIMARY KEY,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
email TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE "address"(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES "user"(id),
|
||||
street TEXT NOT NULL,
|
||||
city TEXT NOT NULL,
|
||||
country TEXT NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
GET http://localhost:8080
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "Content-Type" contains "text/html"
|
||||
body contains "SQLPage Form Demo"
|
||||
body contains "Users"
|
||||
body contains "Add a new user"
|
||||
body not contains "An error occurred"
|
||||
|
||||
POST http://localhost:8080/insert_user.sql?id=987654
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
`First%20name=Hurl&Last%20name=Masterdetail&Email=hurl-masterdetail%40example.com`
|
||||
HTTP 302
|
||||
[Asserts]
|
||||
header "Location" == "edit_user.sql?id=987654"
|
||||
|
||||
GET http://localhost:8080/edit_user.sql?id=987654
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Edit user"
|
||||
body contains "Hurl"
|
||||
body contains "Masterdetail"
|
||||
body contains "hurl-masterdetail@example.com"
|
||||
body contains "Addresses"
|
||||
body contains "Add address"
|
||||
body not contains "An error occurred"
|
||||
|
||||
POST http://localhost:8080/insert_address.sql?user_id=987654
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
`Street=42%20Hurl%20Street&City=Testville&Country=Exampleland`
|
||||
HTTP 302
|
||||
[Asserts]
|
||||
header "Location" == "edit_user.sql?id=987654"
|
||||
|
||||
GET http://localhost:8080/edit_user.sql?id=987654
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Hurl"
|
||||
body contains "Masterdetail"
|
||||
body contains "42 Hurl Street, Testville, Exampleland"
|
||||
body not contains "An error occurred"
|
||||
|
||||
POST http://localhost:8080/insert_user.sql?id=987654
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
`First%20name=Hurl&Last%20name=Updated&Email=hurl-updated-masterdetail%40example.com`
|
||||
HTTP 302
|
||||
[Asserts]
|
||||
header "Location" == "edit_user.sql?id=987654"
|
||||
|
||||
GET http://localhost:8080/edit_user.sql?id=987654
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Hurl"
|
||||
body contains "Updated"
|
||||
body contains "hurl-updated-masterdetail@example.com"
|
||||
body contains "42 Hurl Street, Testville, Exampleland"
|
||||
body not contains "Masterdetail"
|
||||
body not contains "An error occurred"
|
||||
|
||||
GET http://localhost:8080
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Hurl Updated"
|
||||
body contains "hurl-updated-masterdetail@example.com"
|
||||
body not contains "An error occurred"
|
||||
Reference in New Issue
Block a user