pub struct Relation<P> { /* private fields */ }Expand description
One relation of a parent resource’s records.
Declared by ResourceDef::relation; the related resource
must be registered on the same panel, which keys the relation by its slug:
let def: ResourceDef<PostResource> =
ResourceDef::new().relation(Relation::has_many::<CommentResource>(lens!(Comment.post_id)));The table is the related resource’s own list table, its columns, search, sort, filters and
pager, over its tenant-scoped query narrowed to the record, titled with its plural label or
label:
- its policy applies as on its own list: no section renders when it refuses
ViewAny, or when the resource is tenant-scoped and the request has no tenant, and each row keeps only the actions its record allows; - a
has_manytable carries the related resource’s View, Edit, Delete and custom actions and its bulk bar, and a create button when the related resource has a form and its policy allowsCreate. The button opens the create page with the owner preselected, as in/admin/comments/-/create?post_id=…: a default for a relationship choice only, validated like any other create; - a
belongs_to_manytable keeps View, Edit and Delete, and offers no custom action and no create button, since a record created there would link nothing; - writes started from the table carry
?return=and land back on the page, which the panel follows only to a path under its own prefix; - its URL parameters take the related resource’s slug as a prefix, as in
?comments.q=, so several tables share one page, and sorting, searching, filtering and paging update it in place. On the edit page that keeps the fields the user edited, but loses repeater rows added and options fetched since the page loaded. A refused save renders no related table.
A has_one relation renders no table: the related resource’s form edits the
record’s one child inside the record’s own form.
tab moves the table off the detail and edit pages onto a page of its own.
Mounting refuses a relation to a resource the panel does not register, and two relations of one resource to the same related resource.
Implementations§
Source§impl<P> Relation<P>
impl<P> Relation<P>
Sourcepub fn has_many<C>(
foreign_key: impl ForeignKey<C::Model, P::PrimaryKey>,
) -> Selfwhere
C: Resource,
pub fn has_many<C>(
foreign_key: impl ForeignKey<C::Model, P::PrimaryKey>,
) -> Selfwhere
C: Resource,
Declare a has_many child resource whose foreign_key holds the owner’s primary key, a
single column.
let comments: Relation<Post> = Relation::has_many::<CommentResource>(lens!(Comment.post_id));Sourcepub fn has_one<C>(foreign_key: impl ForeignKey<C::Model, P::PrimaryKey>) -> Selfwhere
C: Resource,
pub fn has_one<C>(foreign_key: impl ForeignKey<C::Model, P::PrimaryKey>) -> Selfwhere
C: Resource,
Declare a has_one child resource whose foreign_key holds the owner’s primary key, a
single column: the record’s one child, edited by the child resource’s own form inside the
owner’s create and edit forms rather than by a table.
let profile: Relation<User> = Relation::has_one::<ProfileResource>(lens!(Profile.user_id));The related resource’s form renders as a section of the owner’s form, titled with its
label or label, each control posting under the child’s slug, as in
profiles.bio; the foreign key renders no control, since the owner’s key fills it. Saving
the owner writes the child in the same transaction, after the owner: a create creates it,
and an edit updates the record’s child or creates one when it has none. Either way the
child goes through its own resource as on its own pages: its policy, tenancy, field access,
control rules, unique probe, validate_record, record fn and commit hooks. A refusal from
either form rolls both back and renders both with their errors. The owner’s hooks receive
the owner’s change, the child’s its own.
The section renders only while the request may write the child: Create on the related
resource, or View and Update on the record’s child, with the foreign key neither hidden
nor locked by field access. A record with more than one child, or one the related
resource’s query leaves out, renders none. A submission posting under the prefix with no
section is refused, and one posting nothing under it, such as an editable cell’s or an
import row’s, leaves the child alone. The relation renders no table.
Mounting refuses a related resource with no form, one whose form binds no control for the foreign key, a soft-deleting one, one whose form holds a control that needs its own keys or routes (a file field, a repeater, an embedded value, a searchable or dependent choice), an owner with no form, and an owner form key the prefix would shadow.
Sourcepub fn belongs_to_many<C>(via: impl Into<Path<P, List<C::Model>>>) -> Self
pub fn belongs_to_many<C>(via: impl Into<Path<P, List<C::Model>>>) -> Self
Declare a many-to-many child resource whose records the owner links through via, its
#[has_many(via = joins.target)] field: the owner’s linked records, which the table
attaches and detaches as join rows.
let tags: Relation<Post> = Relation::belongs_to_many::<TagResource>(Post::fields().tags());The section’s header carries Attach, whose dialog asks for one record through a
searchable choice offered as a relationship’s options are; attaching a linked record
changes nothing. Each row and the bulk bar carry Detach, which deletes the join rows and
leaves the records. Both post under the owner’s record:
{list}/{key}/-/relations/{slug}/-/actions/attach, …/-/actions/detach with the
selection’s ids, and …/{related}/-/actions/detach for one row. The owner’s hooks
receive Change::Attach or
Change::Detach.
Attaching and detaching ask the owner’s policy for Update, as editing it does: no button
renders without it, and the routes answer 403. The attach dialog offers the related
resource’s rows as its relationship choices do; a record its policy refuses View shows
without a Detach action, as it shows without its other row actions, and is not detached.
Mounting refuses a join model a link cannot write.
Source§impl<P> Relation<P>
impl<P> Relation<P>
Sourcepub fn label(self, label: impl Into<Text>) -> Self
pub fn label(self, label: impl Into<Text>) -> Self
Titles the section label instead of the related resource’s label: its plural one over a
table, its singular one over a has_one relation’s form.
Sourcepub fn tab(self) -> Self
pub fn tab(self) -> Self
Serves the table on a page of its own, {list}/{key}/{related slug}, instead of under the
record’s detail and edit pages, linked from the record’s navigation: the tab bar its detail
page, edit page, record pages and relation tabs share.
The page loads the record as its detail page does, through the resource’s query and
Ability::View, and answers 403 when the related resource’s policy
refuses ViewAny. Mounting refuses a has_one relation as a tab, since its form edits
inside the record’s, and a tab whose related slug is edit, delete, - or a record
page’s.