Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Related Records

A resource lists another entity’s records that belong to each of its own, as a section on the edit page. A has-many section has New, Edit and Delete; a many-to-many section has Attach, Edit and Remove.

# descriptors/products.yaml
relations:
  variants:
    label: Variant
    entity: product_variants
    foreign_key: product_id
    list:
      columns:
        sku: {}
        price: { type: money }
    form:
      fields:
        sku: { rules: [required] }
        price: { type: money }
Resource::new("/products", "Products", list)
    .form(form)
    .permission("acme.manage_products")
    .relation(RelationConfig::has_many("variants", "Variant", "product_variants", "product_id", variants_list, variants_form))
# descriptors/products.yaml
relations:
  tags:
    label: Tag
    entity: tags
    pivot:
      table: product_tags
      parent_key: product_id
      related_key: tag_id
      source: acme.tags
    list:
      columns:
        name: {}
    form:
      fields:
        note: {}
Resource::new("/products", "Products", list)
    .form(form)
    .permission("acme.manage_products")
    .relation(
        RelationConfig::belongs_to_many(
            "tags",
            "Tag",
            "tags",
            PivotConfig::new("product_tags", "product_id", "tag_id", "acme.tags"),
            tags_list,
        )
        .form(note_form),
    )
// The picker source Attach searches, registered with the admin.
Contributions {
    picker_sources: vec![PickerSourceReg::new(
        "acme.tags",
        Arc::new(TableSource::new("tags", "id", "name")),
    )],
    ..Default::default()
}
KeyDefaultDoes
labelrequiredOne related record, singular: the New or Attach button, the child form’s title.
titlethe name, humanizedThe section heading.
entityrequiredThe related table.
foreign_keyone of the twoHas-many: the column on the related table holding the parent’s id. Written by the framework from the URL; a field of that name in form is kept hidden.
pivotone of the twoMany-to-many: the table joining the two, keys below.
listrequiredThe section’s list: the same keys as a resource’s list. Its columns are the related table’s.
formrequired for has-manyHas-many: the related record’s form. Many-to-many: the fields kept on the pivot row, asked for on Attach; empty gives the section no Edit.
permissionthe parent’sA permission the operator must also hold.
Pivot keyDefaultDoes
tablerequiredThe pivot table.
parent_keyrequiredThe column holding the parent’s id. Written by the framework from the URL.
related_keyrequiredThe column holding the related record’s id. Chosen in the picker on Attach, fixed afterwards.
sourcerequiredThe registered picker source Attach searches.
id_fieldidThe pivot row’s id column.
PathDoes
/products/7/editThe parent’s form, then a section per relation.
/products/7/variantsThe section’s list on its own, sorted and paged.
/products/7/variants/newHas-many: a new related record. Many-to-many: Attach, a record from the picker with the pivot fields. Saves return to the parent.
/products/7/variants/3/editHas-many: the related record 3. Many-to-many: pivot row 3’s fields. Only under its own parent.
/products/7/variants/deleteHas-many: deletes the selected records through the related entity’s listeners and persister. Many-to-many: deletes the selected pivot rows the same way; the records stay.
/products/7/variants/reorderHas-many with reorder on its list: moves one record within the parent’s section.

A relation needs a form on the parent. A relation’s name is a path segment and cannot be new, edit, delete, import, export or columns. A create page names the sections it will have; “Save and continue editing” lands on them. The section is never offered for a parent that does not exist. Attaching a record already attached, or one the related table does not hold, is refused with a message.