pub struct NestedChangesetForm<P, C> {
pub changeset: NestedChangeset<P, C>,
/* private fields */
}Expand description
Axum extractor that decodes a nested (has_many) form body, validates the
parent and every non-destroyed child row, and captures the CSRF and
submit-token context for re-rendering.
Mirrors ChangesetForm: errors live in the
NestedChangeset rather than rejecting with 422; the handler decides how
to respond. Only application/x-www-form-urlencoded bodies are accepted
(multipart is a follow-up).
Fields§
§changeset: NestedChangeset<P, C>The validated (or invalid) nested changeset.
Implementations§
Source§impl<P, C: NestedChild> NestedChangesetForm<P, C>
impl<P, C: NestedChild> NestedChangesetForm<P, C>
Sourcepub fn blank(parent: P, csrf_token: Option<String>) -> Self
pub fn blank(parent: P, csrf_token: Option<String>) -> Self
Build a blank nested-form context for the initial new (create) GET
render, before any submission.
Mirrors ChangesetForm::blank: it
wraps parent in a non-validating NestedChangeset::blank (no
child rows, no errors) so the initial page renders clean — no premature
“field is required” message or aria-invalid="true" before the user has
typed. For an edit render that must show existing children, use
seeded instead (blank renders zero child rows).
Contrast the POST path (the NestedChangesetForm extractor /
decode_nested_urlencoded), which validates the submission and
re-renders inline errors.
csrf_token is the token from a CsrfToken extractor, or None when
CSRF middleware is not active. The CSRF and submit-token field names
default to _csrf / _submit_token (exactly what the extractor falls
back to when the corresponding config extensions are absent); when the
app customizes security.csrf.form_field, set it with
with_csrf_field so
form_tag emits the right hidden field name.
The submit token starts None, so a bare blank(..).form_tag(..) emits
no submit-token hidden input and the first submission is not protected
against double-submit (SubmitTokenLayer
passes tokenless mutating requests through). Supply the minted token on the
initial GET with with_submit_token (and
with_submit_field if the field name is
customized) so the first submit carries a token too:
#[get("/orders/new")]
async fn new_order(csrf: CsrfToken, submit: SubmitToken) -> Markup {
let form = NestedChangesetForm::<NewOrder, NewLineItem>::blank(
NewOrder::default(),
Some(csrf.token().to_owned()),
)
.with_submit_token(Some(submit.token().to_owned()));
form.form_tag("/orders", "post", /* … */)
}Sourcepub fn from_changeset(changeset: NestedChangeset<P, C>) -> Self
pub fn from_changeset(changeset: NestedChangeset<P, C>) -> Self
Wrap a pre-built NestedChangeset (which may already carry validation
errors) in a form for rendering, with no CSRF/submit token.
Mirrors ChangesetForm::from_changeset:
useful in tests and cases where a NestedChangeset was produced
externally (e.g. via decode_nested_urlencoded) before constructing a
form for re-render. The CSRF/submit-token field names default to
_csrf / _submit_token; add a token with
with_csrf_field as needed.
Sourcepub fn with_csrf_field(self, field: impl Into<String>) -> Self
pub fn with_csrf_field(self, field: impl Into<String>) -> Self
Override the CSRF form-field name used by form_tag.
Mirrors
ChangesetForm::with_csrf_field:
call this on a blank GET-handler form when
security.csrf.form_field is customized (e.g. "authenticity_token").
The extractor captures the configured name automatically on the POST
path.
Sourcepub fn with_submit_token(self, token: Option<String>) -> Self
pub fn with_submit_token(self, token: Option<String>) -> Self
Supply the one-time submit token to a blank (or
from_changeset) GET-handler form so
form_tag emits the hidden submit-token input on the
initial render — protecting the very first submission against
double-submit, not just later 422 re-renders.
blank leaves this None (the initial page renders no
submit-token field otherwise), and SubmitTokenLayer
passes tokenless mutating requests through unchanged — so without calling
this the first create-form submit is unprotected. Source the token from a
SubmitToken extractor on the GET handler.
The extractor captures it automatically on the POST re-render path.
When the app customizes security.submit_token.field_name, pair this with
with_submit_field so the hidden input carries
the right name.
Sourcepub fn with_submit_field(self, field: impl Into<String>) -> Self
pub fn with_submit_field(self, field: impl Into<String>) -> Self
Override the submit-token form-field name used by
form_tag.
Mirrors with_csrf_field: call this on a
blank GET-handler form when
security.submit_token.field_name is customized (the default is
_submit_token). The extractor captures the configured name automatically
on the POST path.
Sourcepub fn csrf_token(&self) -> Option<&str>
pub fn csrf_token(&self) -> Option<&str>
The CSRF token captured from the request, if the CSRF middleware is active.
Sourcepub fn csrf_field(&self) -> &str
pub fn csrf_field(&self) -> &str
The CSRF form-field name (honours security.csrf.form_field).
Sourcepub fn submit_token(&self) -> Option<&str>
pub fn submit_token(&self) -> Option<&str>
The one-time submit token captured from the request, if the submit-token middleware is active.
Sourcepub fn submit_field(&self) -> &str
pub fn submit_field(&self) -> &str
The submit-token form-field name (honours
security.submit_token.field_name).
Sourcepub fn into_changeset(self) -> NestedChangeset<P, C>
pub fn into_changeset(self) -> NestedChangeset<P, C>
Consume and return only the inner NestedChangeset.
Sourcepub fn into_valid(self) -> Result<(P, Vec<C>), Self>
pub fn into_valid(self) -> Result<(P, Vec<C>), Self>
Return Ok((parent, children)) when valid, Err(self) when not.
The Err branch retains the CSRF/submit context so the handler can
immediately re-render the form with inline errors.
§Errors
Returns Err(self) when the inner changeset has validation errors.
Source§impl<P, C: NestedChild + Serialize> NestedChangesetForm<P, C>
Edit-render seeding for the form context — mirrors
NestedChangeset::seeded, carrying the extra serde::Serialize bound on
this impl block alone.
impl<P, C: NestedChild + Serialize> NestedChangesetForm<P, C>
Edit-render seeding for the form context — mirrors
NestedChangeset::seeded, carrying the extra serde::Serialize bound on
this impl block alone.
Sourcepub fn seeded(parent: P, children: Vec<C>, csrf_token: Option<String>) -> Self
pub fn seeded(parent: P, children: Vec<C>, csrf_token: Option<String>) -> Self
Build a nested-form context for the initial edit GET render,
pre-populated with the existing persisted children.
Mirrors blank’s CSRF handling (the CSRF/submit-token
field names default to _csrf / _submit_token, and the submit token
starts None), but wraps NestedChangeset::seeded instead of
NestedChangeset::blank: the changeset pre-renders one row per existing
child so the edit page shows and preserves current line items — with their
ids carried as hidden inputs (via RowScope::hidden_input) — and the
no-JS _destroy removal of an existing child works before the first
submit.
The same builder methods used with blank apply here:
with_csrf_field for a customized
security.csrf.form_field, and with_submit_token
/ with_submit_field to emit the one-time
submit-token hidden input on the initial edit render. csrf_token is the
token from a CsrfToken extractor, or None when CSRF middleware is not
active.
Source§impl<P, C: NestedChild> NestedChangesetForm<P, C>
Maud rendering — emit the <form> open tag with the captured CSRF (and
submit-token) hidden fields injected.
impl<P, C: NestedChild> NestedChangesetForm<P, C>
Maud rendering — emit the <form> open tag with the captured CSRF (and
submit-token) hidden fields injected.
Sourcepub fn form_tag(&self, action: &str, method: &str, content: Markup) -> Markup
pub fn form_tag(&self, action: &str, method: &str, content: Markup) -> Markup
Render a <form> element wrapping content, injecting the CSRF hidden
input under the captured field name (honouring
security.csrf.form_field) and — when present — the one-time
submit-token hidden input under its captured field name.
Mirrors ChangesetForm::form_tag,
including the PUT/PATCH/DELETE → hidden _method override. Prefer
this over the standalone crate::form::form_tag for a nested-form
re-render: the standalone helper hardcodes the default _csrf field name
and would emit the wrong hidden field for an app that customized
security.csrf.form_field, so the next submit’s CSRF check would reject
the form. This method uses the field name the extractor captured (or the
one set via with_csrf_field on a
blank form), keeping CSRF parity across the re-render.
The submit-token hidden input is emitted only when a submit token is
present. The POST re-render path captures it automatically; on the initial
GET render from blank, supply it with
with_submit_token so the first submit is
protected against double-submit too — otherwise a bare
blank(..).form_tag(..) create form carries no submit token and its first
submission passes through SubmitTokenLayer
unprotected.
Methods from Deref<Target = NestedChangeset<P, C>>§
Sourcepub fn is_valid(&self) -> bool
pub fn is_valid(&self) -> bool
true when the parent is valid, every non-destroyed row has no
errors, and the children all parsed and validated.
Sourcepub fn errors_for(&self, key: &str) -> &[String]
pub fn errors_for(&self, key: &str) -> &[String]
Validation messages for key, or an empty slice.
Supports both parent field keys (delegated to
Changeset::errors_for) and combined child keys of the form
"{COLLECTION}[{i}].{sub}" (e.g. "items[1].quantity"). A bare
"{COLLECTION}[{i}]" returns that row’s row-level (parse) errors.
Sourcepub fn rows(&self) -> &[NestedRow]
pub fn rows(&self) -> &[NestedRow]
The submitted child rows in compacted (ascending-index) order.
Sourcepub fn collection_name(&self) -> &'static str
pub fn collection_name(&self) -> &'static str
The child collection name, i.e. NestedChild::COLLECTION.
Trait Implementations§
Source§impl<P, C> Deref for NestedChangesetForm<P, C>
Dereferences to NestedChangeset<P, C> so all changeset methods are
available directly on the form (form.is_valid(), form.errors_for(…),
form.rows(), …).
impl<P, C> Deref for NestedChangesetForm<P, C>
Dereferences to NestedChangeset<P, C> so all changeset methods are
available directly on the form (form.is_valid(), form.errors_for(…),
form.rows(), …).
Source§impl<S, P, C> FromRequest<S> for NestedChangesetForm<P, C>
impl<S, P, C> FromRequest<S> for NestedChangesetForm<P, C>
Auto Trait Implementations§
impl<P, C> Freeze for NestedChangesetForm<P, C>where
P: Freeze,
impl<P, C> RefUnwindSafe for NestedChangesetForm<P, C>where
P: RefUnwindSafe,
C: RefUnwindSafe,
impl<P, C> Send for NestedChangesetForm<P, C>
impl<P, C> Sync for NestedChangesetForm<P, C>
impl<P, C> Unpin for NestedChangesetForm<P, C>
impl<P, C> UnsafeUnpin for NestedChangesetForm<P, C>where
P: UnsafeUnpin,
impl<P, C> UnwindSafe for NestedChangesetForm<P, C>where
P: UnwindSafe,
C: UnwindSafe,
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<T> AutumnDependents for Twhere
T: ?Sized,
impl<T> AutumnDependents for Twhere
T: ?Sized,
Source§fn dependents() -> &'static [RuntimeDependentSpec]
fn dependents() -> &'static [RuntimeDependentSpec]
#[model] overrides via an inherent shadow when dependents exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T, Conn> RunQueryDsl<Conn> for T
impl<T, Conn> RunQueryDsl<Conn> for T
Source§fn execute<'conn, 'query>(
self,
conn: &'conn mut Conn,
) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
fn execute<'conn, 'query>( self, conn: &'conn mut Conn, ) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
Source§fn load<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn load<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Source§fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
Stream] with the returned rows. Read moreSource§fn get_result<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
fn get_result<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
Source§fn get_results<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn get_results<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Vec with the affected rows. Read moreSource§impl<T> Scoped for T
impl<T> Scoped for T
Source§fn scope(ctx: &PolicyContext) -> ScopeQuery<'_, Self>
fn scope(ctx: &PolicyContext) -> ScopeQuery<'_, Self>
ScopeQuery for this type. Resolves the
registered scope at .load() time, not here.