pub struct ChangesetForm<T> {
pub changeset: Changeset<T>,
/* private fields */
}Expand description
Axum extractor that decodes a form body, runs validation, and captures the CSRF token — all in one step.
Supports both application/x-www-form-urlencoded (always) and
multipart/form-data (when the multipart feature is enabled).
Unlike crate::validation::Valid, this extractor never rejects with
422 — errors live in the Changeset and the handler decides how to
respond. Fails with 400 only when the body cannot be decoded into T at
all.
§CSRF — no extra developer action in POST handlers
The extractor reads the CsrfToken from request extensions (placed there
by crate::security::CsrfLayer). Calling
ChangesetForm::form_tag then emits the hidden _csrf input
automatically — no separate CsrfToken parameter needed.
For GET handlers (new/edit), use ChangesetForm::blank and pass
csrf.token() from a CsrfToken extractor.
§Example
#[post("/users")]
async fn create(form: ChangesetForm<NewUser>) -> impl IntoResponse {
match form.into_valid() {
Ok(user) => { /* persist & redirect */ }
Err(form) => (StatusCode::UNPROCESSABLE_ENTITY,
form.form_tag("/users", "post", html! {
(form.text_input("name", "Name"))
(form.submit_button("Save"))
})).into_response()
}
}Fields§
§changeset: Changeset<T>The validated (or invalid) changeset.
Implementations§
Source§impl<T> ChangesetForm<T>
impl<T> ChangesetForm<T>
Sourcepub fn blank(data: T, csrf_token: &str) -> Self
pub fn blank(data: T, csrf_token: &str) -> Self
Build a blank form context for GET handlers (new / edit).
Wraps data in a valid Changeset and stores csrf_token so that
ChangesetForm::form_tag can emit the hidden input automatically.
#[get("/users/new")]
async fn new_user(csrf: CsrfToken) -> Markup {
let ctx = ChangesetForm::blank(UserForm::default(), csrf.token());
ctx.form_tag("/users", "post", html! { (ctx.text_input("name", "Name")) })
}Sourcepub fn without_csrf(data: T) -> Self
pub fn without_csrf(data: T) -> Self
Construct a display-only ChangesetForm with no CSRF token.
Use this on GET handlers where CSRF middleware is not active, or when
the form will be re-rendered purely for display (e.g. an initial blank
form on a page that does not enforce CSRF). form_tag
will omit the hidden CSRF input when no token is stored.
Sourcepub fn from_changeset(changeset: Changeset<T>) -> Self
pub fn from_changeset(changeset: Changeset<T>) -> Self
Wrap a pre-built Changeset (which may already carry validation errors)
in a ChangesetForm without a CSRF token.
Useful in tests and cases where a Changeset was produced externally
(e.g. via IntoChangeset) before constructing a form for rendering.
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 ChangesetForm::form_tag.
Call this when security.csrf.form_field is set to something other than
"_csrf" (e.g. "authenticity_token"). The CsrfFormField extension
populated by from_request sets this automatically
for POST handlers; use this builder on GET handlers that construct a blank
form with blank.
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 into_changeset(self) -> Changeset<T>
pub fn into_changeset(self) -> Changeset<T>
Consume and return only the inner Changeset.
Sourcepub fn into_valid(self) -> Result<T, Self>
pub fn into_valid(self) -> Result<T, Self>
Return Ok(T) if the changeset is valid, Err(self) if not.
The Err branch returns the whole ChangesetForm (with its CSRF
token) so the handler can immediately call form.form_tag() to
re-render with inline errors.
§Errors
Returns Err(self) when the inner changeset has field-level validation errors.
Source§impl<T: Serialize> ChangesetForm<T>
Maud rendering methods — emit form HTML with automatic CSRF injection.
impl<T: Serialize> ChangesetForm<T>
Maud rendering methods — emit form HTML with automatic CSRF injection.
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 with the stored CSRF token injected as a
hidden input — the field name honours security.csrf.form_field from
config, so no developer action is required even for non-default names.
Sourcepub fn text_input(&self, field: &str, label: &str) -> Markup
pub fn text_input(&self, field: &str, label: &str) -> Markup
Render a labeled <input type="text"> for field using the stored
changeset (value + errors).
Render a <button type="submit"> with label.
Methods from Deref<Target = Changeset<T>>§
Sourcepub fn errors_for(&self, field: &str) -> &[String]
pub fn errors_for(&self, field: &str) -> &[String]
Returns the validation messages for field, or an empty slice.
Sourcepub fn errors(&self) -> &HashMap<String, Vec<String>>
pub fn errors(&self) -> &HashMap<String, Vec<String>>
All field errors as a map (field name → list of messages).
Sourcepub fn field_value(&self, field: &str) -> Option<String>
pub fn field_value(&self, field: &str) -> Option<String>
Serialize the value of field from the inner data to a String.
Used by rendering helpers to re-populate <input value="…"> after a
failed submission. Returns None for missing or non-scalar fields.
Trait Implementations§
Source§impl<T> Deref for ChangesetForm<T>
Dereferences to Changeset<T> so all changeset methods are available
directly on ChangesetForm<T> — form.is_valid(), form.errors_for(…),
etc.
impl<T> Deref for ChangesetForm<T>
Dereferences to Changeset<T> so all changeset methods are available
directly on ChangesetForm<T> — form.is_valid(), form.errors_for(…),
etc.
Source§impl<S, T> FromRequest<S> for ChangesetForm<T>
impl<S, T> FromRequest<S> for ChangesetForm<T>
Auto Trait Implementations§
impl<T> Freeze for ChangesetForm<T>where
T: Freeze,
impl<T> RefUnwindSafe for ChangesetForm<T>where
T: RefUnwindSafe,
impl<T> Send for ChangesetForm<T>where
T: Send,
impl<T> Sync for ChangesetForm<T>where
T: Sync,
impl<T> Unpin for ChangesetForm<T>where
T: Unpin,
impl<T> UnsafeUnpin for ChangesetForm<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for ChangesetForm<T>where
T: 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> 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
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
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,
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.