Databases
Validate SeaORM ActiveModels before insert and update, manually or in before_save.

vld-sea turns the Set / Unchanged values of a SeaORM ActiveModel into a JSON object and validates it against a vld schema, so invalid data never reaches insert() / update().

Install

Cargo.toml
[dependencies]
vld = "0.5"
vld-sea = "0.5"  # SeaORM 1 by default
sea-orm = { version = "1", features = ["macros", "sqlx-sqlite", "runtime-tokio-rustls"] }

Features

FeatureDefaultEffect
sea-orm-1yesBuild against SeaORM 1
sea-orm-2Build against SeaORM 2 (Rust 1.94+)
with-jsonMap JSON columns
with-chronoMap chrono date / time / datetime columns
with-timeMap time date / time / datetime columns
with-uuidMap UUID columns
with-rust_decimalMap rust_decimal columns
with-bigdecimalMap bigdecimal columns
postgres-arrayMap Postgres array columns

Exactly one of sea-orm-1 / sea-orm-2 must be enabled. Each with-* feature also enables the SeaORM feature of the same name. Without it, columns of that type are passed to the schema as null.

vld-sea = { version = "0.5", default-features = false, features = ["sea-orm-2", "with-chrono", "with-uuid"] }

MSRV: 1.85 with sea-orm-1, 1.94 with sea-orm-2 (see MSRV).

Example

use sea_orm::Set;
use vld_sea::prelude::*;

vld::schema! {
    #[derive(Debug, Clone)]
    pub struct UserInput {
        pub name: String  => vld::string().min(1).max(100),
        pub email: String => vld::string().email(),
    }
}

mod user {
    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
    #[sea_orm(table_name = "users")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i32,
        pub name: String,
        pub email: String,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    // Validate on every insert / update:
    vld_sea::impl_vld_before_save!(ActiveModel, super::UserInput);
}

fn check() {
    let am = user::ActiveModel {
        id: Set(1),
        name: Set("".to_owned()),
        email: Set("not-an-email".to_owned()),
    };
    assert!(validate_active::<UserInput, _>(&am).is_err());
}

API

  • validate_active::<S, A>(&active_model) — validate the Set / Unchanged fields; returns the parsed S.
  • validate_model::<S, T>(&value) — validate any Serialize value (a Model, an input DTO); returns S.
  • validate_json::<S>(&json) — validate a raw serde_json::Value.
  • before_save::<S, A>(&active_model) — like validate_active, but returns Result<(), DbErr> for use inside ActiveModelBehavior::before_save.
  • impl_vld_before_save!(ActiveModel, Schema) / impl_vld_before_save!(ActiveModel, insert: A, update: B) — implement ActiveModelBehavior with validation in before_save.
  • active_model_to_json(&active_model) — the JSON object the schema sees.
  • Validated<S, T> — validated wrapper (new, inner, into_inner, Deref, Serialize).
  • VldSeaError — Validation(VldError) or Serialization(String); converts into DbErr::Custom.
  • vld_sea::sea_orm — the selected SeaORM crate, re-exported.

Gotchas

  • NotSet fields are omitted. For partial updates, use a schema whose fields are .optional(), or the insert: / update: form of impl_vld_before_save! with a separate update schema.
  • Column names are the entity's column names (Column::as_str()), not Rust field names, if the two differ.
  • Values map like serde_json::to_value of the Rust type: Bytes as an array of u8, chrono / time dates as YYYY-MM-DD, zoned datetimes as RFC 3339, UUIDs and decimals as strings, f32 through its decimal form. On SeaORM 2, ActiveEnum values map to their string value.
  • A column type whose with-* feature is off, and a None value, become null.