Saltar al contenido principal
Las relaciones conectan dos objetos entre sí. En Twenty, las relaciones siempre son bidireccionales: cada relación tiene dos lados, y cada lado se declara como un campo que hace referencia al otro.
Tipo de relaciónDescripción¿Tiene clave foránea?
MANY_TO_ONEMuchos registros de este objeto apuntan a un registro del objeto de destinoSí (joinColumnName)
ONE_TO_MANYUn registro de este objeto tiene muchos registros del objeto de destinoNo (lado inverso)

Cómo funcionan las relaciones

Cada relación requiere dos campos que se referencian entre sí:
  1. El lado MANY_TO_ONE — vive en el objeto que contiene la clave foránea.
  2. El lado ONE_TO_MANY — vive en el objeto que es propietario de la colección.
Ambos campos usan FieldType.RELATION y se hacen referencia cruzada mediante relationTargetFieldMetadataUniversalIdentifier.

Ejemplo: la tarjeta postal tiene muchos destinatarios

Un PostCard puede enviarse a muchos registros PostCardRecipient. Cada destinatario pertenece exactamente a una tarjeta postal. Paso 1: Define el lado ONE_TO_MANY en PostCard (el lado “uno”):
src/fields/post-card-recipients-on-post-card.field.ts
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';

// Export so the other side can reference it
export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111';
// Import from the other side
import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field';

export default defineField({
  universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
  objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
  type: FieldType.RELATION,
  name: 'postCardRecipients',
  label: 'Post Card Recipients',
  icon: 'IconUsers',
  relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
  relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID,
  universalSettings: {
    relationType: RelationType.ONE_TO_MANY,
  },
});
Paso 2: Define el lado MANY_TO_ONE en PostCardRecipient (el lado “muchos” — contiene la clave foránea):
src/fields/post-card-on-post-card-recipient.field.ts
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';

// Export so the other side can reference it
export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222';
// Import from the other side
import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field';

export default defineField({
  universalIdentifier: POST_CARD_FIELD_ID,
  objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
  type: FieldType.RELATION,
  name: 'postCard',
  label: 'Post Card',
  icon: 'IconMail',
  relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
  relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
  universalSettings: {
    relationType: RelationType.MANY_TO_ONE,
    onDelete: OnDeleteAction.CASCADE,
    joinColumnName: 'postCardId',
  },
});
Importaciones circulares: ambos campos de relación hacen referencia al universalIdentifier del otro. Para evitar problemas de importaciones circulares, exporta los ID de tus campos como constantes con nombre desde cada archivo e impórtalos en el otro. El sistema de compilación los resuelve en tiempo de compilación.

Relacionar con objetos estándar

Para crear una relación con un objeto integrado de Twenty (Person, Company, etc.), usa STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS:
src/fields/person-on-self-hosting-user.field.ts
import {
  defineField,
  FieldType,
  RelationType,
  OnDeleteAction,
  STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object';

export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333';
export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444';

export default defineField({
  universalIdentifier: PERSON_FIELD_ID,
  objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER,
  type: FieldType.RELATION,
  name: 'person',
  label: 'Person',
  description: 'Person matching with the self hosting user',
  isNullable: true,
  relationTargetObjectMetadataUniversalIdentifier:
    STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
  relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID,
  universalSettings: {
    relationType: RelationType.MANY_TO_ONE,
    onDelete: OnDeleteAction.SET_NULL,
    joinColumnName: 'personId',
  },
});

Propiedades del campo de relación

PropiedadObligatorioDescripción
typeDebe ser FieldType.RELATION
relationTargetObjectMetadataUniversalIdentifierEl universalIdentifier del objeto de destino
relationTargetFieldMetadataUniversalIdentifierEl universalIdentifier del campo correspondiente en el objeto de destino
universalSettings.relationTypeRelationType.MANY_TO_ONE o RelationType.ONE_TO_MANY
universalSettings.onDeleteSolo para MANY_TO_ONEQué sucede cuando se elimina el registro referenciado: CASCADE, SET_NULL, RESTRICT o NO_ACTION
universalSettings.joinColumnNameSolo para MANY_TO_ONENombre de la columna de base de datos para la clave foránea (p. ej., postCardId)

Campos de relación en línea

También puedes declarar una relación directamente dentro de defineObject. Cuando es en línea, omite objectUniversalIdentifier — se hereda del objeto padre:
export default defineObject({
  universalIdentifier: '...',
  nameSingular: 'postCardRecipient',
  // ...
  fields: [
    {
      universalIdentifier: POST_CARD_FIELD_ID,
      type: FieldType.RELATION,
      name: 'postCard',
      label: 'Post Card',
      relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
      relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
      universalSettings: {
        relationType: RelationType.MANY_TO_ONE,
        onDelete: OnDeleteAction.CASCADE,
        joinColumnName: 'postCardId',
      },
    },
    // … other fields
  ],
});