Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
171 lines
6.2 KiB
Plaintext
171 lines
6.2 KiB
Plaintext
---
|
|
title: Create Many-to-Many Relations
|
|
description: Connect records where many items on both sides can be linked together using junction objects.
|
|
---
|
|
|
|
Many-to-many relations let you connect multiple records on both sides. For example: many People can work on many Projects, and each Project can have many People.
|
|
|
|
<Warning>
|
|
**Beta Feature**: Junction relations are currently in beta. Enable them at **Settings → Community → Features** before following this guide.
|
|
</Warning>
|
|
|
|
<Note>
|
|
This feature also requires **Advanced mode** to be enabled (toggle at the bottom left of Settings).
|
|
</Note>
|
|
|
|
## When to Use Many-to-Many
|
|
|
|
Use many-to-many when both sides of a relationship can have multiple connections:
|
|
|
|
| Relationship | Example |
|
|
|-------------|---------|
|
|
| People ↔ Projects | A person works on multiple projects; a project has multiple team members |
|
|
| Companies ↔ Tags | A company can have multiple tags; a tag can apply to multiple companies |
|
|
| Products ↔ Orders | A product can be in multiple orders; an order contains multiple products |
|
|
|
|
## How It Works
|
|
|
|
Twenty uses a **junction object** pattern for many-to-many relations. A junction object sits between two objects and holds the connections:
|
|
|
|
```
|
|
People ←→ Project Assignments ←→ Projects
|
|
```
|
|
|
|
The **Project Assignments** object (junction) has:
|
|
- A relation to People (many-to-one)
|
|
- A relation to Projects (many-to-one)
|
|
|
|
When you enable the junction relation toggle, Twenty displays linked records directly instead of showing the intermediate junction records.
|
|
|
|
## Prerequisites
|
|
|
|
1. **Enable Junction Relations in Beta Features**: Go to **Settings → Community → Features** and enable **Junction Relations**
|
|
2. **Enable Advanced mode**: Toggle on **Advanced mode** at the bottom left of the Settings sidebar
|
|
3. Plan your data model:
|
|
- Which two objects are you connecting?
|
|
- What should the junction object be called?
|
|
|
|
## Step 1: Create the Junction Object
|
|
|
|
First, create the intermediate object that will hold the connections.
|
|
|
|
1. Go to **Settings → Data Model**
|
|
2. Click **+ Add object**
|
|
3. Name it descriptively (e.g., "Project Assignment", "Team Member", "Product Order")
|
|
4. Toggle "Skip creating a Name field" on
|
|
|
|
<img src="/images/user-guide/fields/new-pivot-object.png" alt="New pivot object" />
|
|
|
|
5. Click **Save**
|
|
|
|
<Tip>
|
|
**Naming convention**: Use a name that describes the relationship, like "Project Assignment" or "Team Membership". This makes the data model easier to understand.
|
|
</Tip>
|
|
|
|
## Step 2: Create Relations Between Objects and the Junction
|
|
|
|
Add relation fields from each of your two objects to the junction object.
|
|
|
|
### First Relation (Object A → Junction)
|
|
|
|
1. Select your first object in **Settings → Data Model**
|
|
2. Click **+ Add Relation**
|
|
3. Select the junction object (e.g., "Project Assignments")
|
|
4. Set the relation type to **One-To-Many** (one person can link to many assignments)
|
|
5. Name the fields:
|
|
- Field on People: e.g., "Project Assignments"
|
|
- Field on junction: e.g., "Person"
|
|
6. Click **Save**
|
|
|
|
### Second Relation (Object B → Junction)
|
|
|
|
1. Select your second object in **Settings → Data Model**
|
|
2. Click **+ Add Relation**
|
|
3. Select the junction object (e.g., "Project Assignments")
|
|
4. Set the relation type to **One-To-Many** (one project can link to many assignments)
|
|
5. Enable **"This is a relation to a Junction Object"**
|
|
|
|
<img src="/images/user-guide/fields/junction-relation-toggle.png" style={{width:'100%'}}/>
|
|
|
|
6. Name the fields:
|
|
- Field on junction: e.g., "Project"
|
|
- Field on Projects: e.g., "Team Members"
|
|
7. Click **Save**
|
|
|
|
## Step 3: Configure the Junction Relation Display
|
|
|
|
Now configure the source objects to display linked records directly, skipping the intermediate junction object.
|
|
|
|
1. Go to **Settings → Data Model**
|
|
2. Select the first object (e.g., "People")
|
|
3. Find the relation field pointing to the junction object (e.g., "Project Assignments")
|
|
4. Click to edit the field
|
|
5. Enable **"This is a relation to a Junction Object"**
|
|
6. Select the **Target relation** (e.g., "Project" — the field on the junction that points to the other side)
|
|
7. Click **Save**
|
|
|
|
## Result
|
|
|
|
After configuration:
|
|
|
|
- On a **Person** record, the "Project Assignments" field displays **Projects** directly (not assignment records)
|
|
- On a **Project** record, the "Team Members" field displays **People** directly
|
|
|
|
The junction object still exists and stores the connections, but the UI presents a cleaner many-to-many view.
|
|
|
|
## Example: People ↔ Projects
|
|
|
|
Here's a complete walkthrough:
|
|
|
|
### Create the Junction Object
|
|
- Name: **Project Assignment**
|
|
- Description: "Links people to projects they work on"
|
|
|
|
### Add Relations
|
|
1. **People → Project Assignment**
|
|
- Type: One-to-Many
|
|
- Field on People: "Project Assignments"
|
|
- Field on Assignment: "Person"
|
|
|
|
2. **Projects → Project Assignment**
|
|
- Type: One-to-Many
|
|
- Field on Projects: "Team Members"
|
|
- Field on Assignment: "Project"
|
|
|
|
### Configure Junction Display
|
|
1. On **People** object:
|
|
- Edit "Project Assignments" field
|
|
- Enable junction toggle
|
|
- Target: "Project"
|
|
|
|
2. On **Projects** object:
|
|
- Edit "Team Members" field
|
|
- Enable junction toggle
|
|
- Target: "Person"
|
|
|
|
### Use It
|
|
- Open a Person record → See their Projects directly
|
|
- Open a Project record → See team members directly
|
|
- Create new connections from either side
|
|
|
|
## Adding Extra Data to Connections
|
|
|
|
Since the junction object is a real object, you can add custom fields to store information about the relationship:
|
|
|
|
- **Role**: "Developer", "Designer", "Manager"
|
|
- **Start Date**: When the person joined the project
|
|
- **Hours Allocated**: Weekly hours on this project
|
|
|
|
To access this data, navigate to the junction object directly or query it via the API.
|
|
|
|
## Limitations
|
|
|
|
- **CSV Import/Export**: Importing many-to-many relations directly is not supported. Import records to the junction object instead.
|
|
- **Filters**: Filtering by many-to-many relations may have limited options.
|
|
|
|
## Related
|
|
|
|
- [Relation Fields](/user-guide/data-model/capabilities/relation-fields) — relation types explained
|
|
- [Create Custom Objects](/user-guide/data-model/how-tos/create-custom-objects) — how to create objects
|
|
- [Create Relation Fields](/user-guide/data-model/how-tos/create-relation-fields) — basic relation setup
|