Skip to main content

NestedChangesetForm

Struct NestedChangesetForm 

Source
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>

Source

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", /* … */)
}
Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn csrf_token(&self) -> Option<&str>

The CSRF token captured from the request, if the CSRF middleware is active.

Source

pub fn csrf_field(&self) -> &str

The CSRF form-field name (honours security.csrf.form_field).

Source

pub fn submit_token(&self) -> Option<&str>

The one-time submit token captured from the request, if the submit-token middleware is active.

Source

pub fn submit_field(&self) -> &str

The submit-token form-field name (honours security.submit_token.field_name).

Source

pub fn into_changeset(self) -> NestedChangeset<P, C>

Consume and return only the inner NestedChangeset.

Source

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.

Source

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.

Source

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>>§

Source

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.

Source

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.

Source

pub fn rows(&self) -> &[NestedRow]

The submitted child rows in compacted (ascending-index) order.

Source

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(), …).

Source§

type Target = NestedChangeset<P, C>

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.
Source§

impl<S, P, C> FromRequest<S> for NestedChangesetForm<P, C>

Source§

type Rejection = Response<Body>

If the extractor fails it’ll use this “rejection” type. A rejection is a kind of error that can be converted into a response.
Source§

async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection>

Perform the extraction.

Auto Trait Implementations§

§

impl<P, C> Freeze for NestedChangesetForm<P, C>
where P: Freeze,

§

impl<P, C> RefUnwindSafe for NestedChangesetForm<P, C>

§

impl<P, C> Send for NestedChangesetForm<P, C>
where P: Send, C: Send,

§

impl<P, C> Sync for NestedChangesetForm<P, C>
where P: Sync, C: Sync,

§

impl<P, C> Unpin for NestedChangesetForm<P, C>
where P: Unpin, C: Unpin,

§

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

Source§

fn aggregate_distinct(self) -> Self::Output
where Self: DistinctDsl,

DISTINCT modifier for aggregate functions Read more
Source§

fn aggregate_all(self) -> Self::Output
where Self: AllDsl,

ALL modifier for aggregate functions Read more
Source§

fn aggregate_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add an aggregate function filter Read more
Source§

fn aggregate_order<O>(self, o: O) -> Self::Output
where Self: OrderAggregateDsl<O>,

Add an aggregate function order Read more
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> AutumnDependents for T
where T: ?Sized,

Source§

fn dependents() -> &'static [RuntimeDependentSpec]

The model’s dependent-cascade specs, in declaration order. Defaults to none; #[model] overrides via an inherent shadow when dependents exist.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoSql for T

Source§

fn into_sql<T>(self) -> Self::Expression

Convert self to an expression for Diesel’s query builder. Read more
Source§

fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
where &'a Self: AsExpression<T>, T: SqlType + TypedExpressionType,

Convert &self to an expression for Diesel’s query builder. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, Conn> RunQueryDsl<Conn> for T

Source§

fn execute<'conn, 'query>( self, conn: &'conn mut Conn, ) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
where Conn: AsyncConnectionCore + Send, Self: ExecuteDsl<Conn> + 'query,

Executes the given command, returning the number of rows affected. Read more
Source§

fn load<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
where U: Send, Conn: AsyncConnectionCore, Self: LoadQuery<'query, Conn, U> + 'query,

Executes the given query, returning a Vec with the returned rows. Read more
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,

Executes the given query, returning a [Stream] with the returned rows. Read more
Source§

fn get_result<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
where U: Send + 'conn, Conn: AsyncConnectionCore, Self: LoadQuery<'query, Conn, U> + 'query,

Runs the command, and returns the affected row. Read more
Source§

fn get_results<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
where U: Send, Conn: AsyncConnectionCore, Self: LoadQuery<'query, Conn, U> + 'query,

Runs the command, returning an Vec with the affected rows. Read more
Source§

fn first<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<<Self::Output as LoadQuery<'query, Conn, U>>::LoadFuture<'conn>, LoadNext<Pin<Box<<Self::Output as LoadQuery<'query, Conn, U>>::Stream<'conn>>>>>
where U: Send + 'conn, Conn: AsyncConnectionCore, Self: LimitDsl, Self::Output: LoadQuery<'query, Conn, U> + Send + 'query,

Attempts to load a single record. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scoped for T
where T: Send + Sync + 'static,

Source§

fn scope(ctx: &PolicyContext) -> ScopeQuery<'_, Self>

Open a deferred ScopeQuery for this type. Resolves the registered scope at .load() time, not here.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WindowExpressionMethods for T

Source§

fn over(self) -> Self::Output
where Self: OverDsl,

Turn a function call into a window function call Read more
Source§

fn window_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add a filter to the current window function Read more
Source§

fn partition_by<E>(self, expr: E) -> Self::Output
where Self: PartitionByDsl<E>,

Add a partition clause to the current window function Read more
Source§

fn window_order<E>(self, expr: E) -> Self::Output
where Self: OrderWindowDsl<E>,

Add a order clause to the current window function Read more
Source§

fn frame_by<E>(self, expr: E) -> Self::Output
where Self: FrameDsl<E>,

Add a frame clause to the current window function Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more