Rustango docs
← Cookbook

Chapter 7 — Forms + serializer

6 tests covering ModelFormFor<T> parse/from_json/error aggregation/ bound-validation/null handling/insert-query emission. No DB needed. Run with cargo test --test cookbook_chapter07_forms.

The forms core, the serializer derive, and each serializer extension (nested / method / many / validators / ViewSet wiring) all ship with passing tests:

$ cargo test --test cookbook_chapter07_forms \
             --test cookbook_chapter07b_serializer \
             --test cookbook_chapter07f_serializer_method_and_validators \
             --test cookbook_chapter07g_nested_serializer \
             --test cookbook_chapter07h_many_serializer

cookbook_chapter07_forms .................................. ok (6)
cookbook_chapter07b_serializer ............................ ok (6)
cookbook_chapter07f_serializer_method_and_validators ...... ok (4)
cookbook_chapter07g_nested_serializer ..................... ok (4)
cookbook_chapter07h_many_serializer ....................... ok (4)
  • §7.95 ModelFormFor::<T>::parse(&HashMap<String,String>) — form- encoded payload → (columns, values) with per-field bound validation. Auto PK and auto_now_add columns are skipped (DB fills them). → modelform_parses_form_encoded_into_typed_values
  • §7.95 missing required fields aggregate into FormErrors, one entry per field. → modelform_missing_required_fields_aggregate_errors
  • §7.96 ModelFormFor::<T>::from_json(&serde_json::Value) — JSON request body. Null values on Option<T> write explicit SqlValue::Null. → modelform_from_json_parses_object, modelform_from_json_null_writes_explicit_null
  • §7.98 min/max bounds run at parse time. Out-of-range values land in FormErrors keyed by field. → modelform_bound_violation_lands_in_form_errors
  • §7.99 into_insert_query() emits an InsertQuery against the model's table. → modelform_into_insert_query_targets_model_table

Note: ModelFormFor::parse skips every auto-populated field — auto_now_add timestamps like joined_at: Auto<DateTime<Utc>> and Auto<T> primary keys — because the database fills them on INSERT. Your form only has to carry the fields a user actually enters.

7.99b #[derive(Serializer)] — DRF-shape JSON façade

6 tests in tests/cookbook_chapter07b_serializer.rs exercise the serializer derive against the cookbook's Author model. Run with cargo test --test cookbook_chapter07b_serializer.

  • from_model + to_value round-trip — every field maps from the model and lands in the JSON output. → serializer_from_model_then_to_value_round_trip
  • #[serializer(read_only)] — excluded from writable_fields(), still appears in JSON output. → read_only_field_omitted_from_writable_fields
  • #[serializer(write_only)] — excluded from JSON output, accepted on input. → write_only_field_excluded_from_json_output
  • #[serializer(source = "x")] — renames the JSON key to a different model field. → source_attribute_renames_json_key
  • #[serializer(skip)] — uses Default::default(), leaves the field in JSON but excludes it from writable_fields(). → skip_field_uses_default_and_appears_in_json_unchanged
  • many_to_value — batches a Vec<Model> into a JSON array. → many_to_value_batches_into_json_array

Beyond the core derive, #[derive(Serializer)] also supports:

  1. Nested FK#[serializer(nested)] on a field whose type is itself a serializer and whose model source is a ForeignKey<Parent> emits the nesting glue. The FK must be loaded first (.get(&pool) or .select_related(...)). → tests/cookbook_chapter07g_nested_serializer.rs
  2. Computed fields#[serializer(method = "fn_name")] calls an inherent fn(model: &T) -> FieldType during from_model. → tests/cookbook_chapter07f_serializer_method_and_validators.rs
  3. ViewSet ↔ Serializer wiringViewSet::for_model(...).serializer::<S>() runs list/detail output through the serializer instead of the bare model. → tests/cookbook_chapter09b_viewset_serializer.rs
  4. Collections (M2M / one-to-many)#[serializer(many = ChildSerializer)] emits a typed set_<field>(&mut self, &[ChildModel]) setter; fetch the children (accessors are async), call the setter, then serialize. → tests/cookbook_chapter07h_many_serializer.rs
  5. Per-field validators#[serializer(validate = "fn_name")] runs a per-field validator, on top of model-level min/max/max_length. → tests/cookbook_chapter07f_serializer_method_and_validators.rs