SeaQL/sea-orm
 Watch   
 Star   
 Fork   
2 days ago
sea-orm

sea-orm-cli@2.0.3

update examples

2 days ago
sea-orm

2.0.3

SeaORM 2.0.3

(since 2.0.2)

Bug Fixes

select_also / select_both with a cast column

Selecting two models panicked when a column needed a cast on select. A combined select derives each A_ / B_ prefixed alias from the selected expression, and a cast is neither a plain column nor an enum cast, so that derivation gave up. Columns now carry their own alias into the combined select and the prefix is applied to it https://github.com/SeaQL/sea-orm/pull/3194

Entity::find().select_also(other::Entity).build(DbBackend::Postgres)
// before: panicked with "cannot apply alias for expr other than Column or AsEnum"
-- after
SELECT "hello"."id" AS "A_id", CAST("hello"."two" AS integer) AS "A_two", ...

Derive macro hygiene

Generated code referred to bindings by plain name, so a field that happened to share a name with one of them shadowed it and the expansion failed to compile. The generated bindings are now hygienic https://github.com/SeaQL/sea-orm/pull/3185

// did not compile before: `row` and `pre` collided with the generated bindings
#[derive(FromQueryResult)]
struct QueryResultProjection {
    row: String,
    pre: String,
}

DeriveActiveEnum with an Error variant

The generated TryFrom<&str> impl wrote its error type as Self::Error, which an Error variant on the enum shadowed. The impl now names sea_orm::DbErr outright https://github.com/SeaQL/sea-orm/pull/3185

#[derive(Debug, EnumIter, DeriveActiveEnum, Eq, PartialEq)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "error_variant")]
enum ErrorVariantEnum {
    #[sea_orm(string_value = "error")]
    Error, // no longer breaks the generated `TryFrom<&str>`
}

ModelEx and explicitly requested Eq

Eq is deliberately not inherited from the model, because a nested relation may hold non-Eq fields — but the filter also discarded an Eq asked for explicitly. An explicit derive is now kept, and the clippy::derive_partial_eq_without_eq warning that the missing Eq triggered is silenced https://github.com/SeaQL/sea-orm/pull/3193

#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "user", model_ex_attrs(derive(Eq)))]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
} // ModelEx now derives Eq

PostgreSQL partial unique indexes in entity codegen

A unique index with a WHERE predicate was generated as an unconditional unique column, claiming a constraint the database does not enforce. Partial indexes are now skipped, since codegen cannot express the predicate https://github.com/SeaQL/sea-orm/pull/3196

CREATE UNIQUE INDEX login_human_login_id_key
    ON login (human_login_id)
    WHERE human_login_id IS NOT NULL; -- no longer emits #[sea_orm(unique)]

postgres:db_name connection URLs

The shorthand form has no authority component, so it yields no URL path segments and the CLI reported it as having no database name. The whole path is now read as the database name https://github.com/SeaQL/sea-orm/pull/3197

sea-orm-cli generate entity -u postgres:my_db -o src/entities

Compatibility Notes

  • The alias fix changes emitted SQL for ordinary single-model queries too: a column with a select_as cast is now followed by AS "<column>". Results are unchanged, but tests asserting on generated SQL strings will need updating.

    -- Entity::find(), before
    SELECT "hello"."id", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello"
    
    -- after
    SELECT "hello"."id", CAST("hello"."two" AS integer) AS "two", "hello"."three3" FROM "hello"
  • No public API was added, removed, or changed in sea-orm, sea-orm-migration, sea-orm-codegen, sea-orm-macros or sea-orm-cli.

2026-08-13 05:16:25
sea-orm

sea-orm-cli@2.0.2

Add changelog for 2.0.2

2026-08-13 04:29:59
sea-orm

2.0.2

SeaORM 2.0.2

Enhancements

  • Add require_one to fetch exactly one row, erroring if none: a non-optional counterpart to one() that returns the item directly and yields DbErr::RecordNotFound when no row matches, so call sites can use ? instead of unwrapping an Option. Available on Selector / SelectorRaw and the Select, SelectTwo, and SelectTwoRequired wrappers https://github.com/SeaQL/sea-orm/pull/3164
  • Add date_time_default_now schema helper — a column defaulting to Expr::current_timestamp() https://github.com/SeaQL/sea-orm/pull/3159
  • Add timestamp_default_now and timestamp_with_time_zone_default_now schema helpers, mirroring date_time_default_now for the timestamp family https://github.com/SeaQL/sea-orm/pull/3165

Bug Fixes

  • CLI: deduplicate grouped vs individual imports when regenerating entities with --preserve-user-modifications, so a user-grouped use foo::{A, B} is recognised as equivalent to the freshly generated use foo::A; use foo::B; and no longer emitted twice https://github.com/SeaQL/sea-orm/pull/3163
2026-08-03 04:18:56
sea-orm

sea-orm-cli@2.0.1

Tag seaography example sea-orm version for bump.sh; tolerate taplo padding

2026-08-03 03:49:25
sea-orm

2.0.1

SeaORM 2.0.1

Enhancements

  • Add set_page to Paginator to set the current page https://github.com/SeaQL/sea-orm/pull/2963
  • Add as_option / into_option to ActiveValue<Option<V>>, flattening the outer active-value state and the inner option https://github.com/SeaQL/sea-orm/pull/3155
  • Add set_unset and friends to ActiveValue: set the value only when currently NotSet https://github.com/SeaQL/sea-orm/pull/3083
  • Add is_set_and / is_unchanged_and to ActiveValue https://github.com/SeaQL/sea-orm/pull/3125
  • Add ConnectOptions::test_before_acquire_if_idle_for(Duration) — ping a pooled connection before it is handed out only once it has been idle for at least the given duration, instead of on every acquire; setting it disables test_before_acquire. Also map_sqlx_postgres_before_acquire / map_sqlx_mysql_before_acquire / map_sqlx_sqlite_before_acquire to install a per-backend SQLx before_acquire callback (composes with the idle-ping shorthand: idle-ping first, then the callback), plus the corresponding getters https://github.com/SeaQL/sea-orm/pull/3143
  • Add MigratorTrait::get_pending_migrations_read_only / get_applied_migrations_read_only / get_migration_with_status_read_only (and the with-self equivalents) — query migration status without running CREATE TABLE, so a database user without DDL privileges can check pending migrations; if the migration table does not exist, all migrations are reported as pending https://github.com/SeaQL/sea-orm/pull/3144

Bug Fixes

  • Require TransactionTrait::Transaction to be a fixed point, fixing nested-transaction recursion (E0275 / future_not_send) in #[sea_orm::model] generated save methods https://github.com/SeaQL/sea-orm/pull/3153

    Compatibility note: if you implement TransactionTrait yourself, Self must be Sync, and your Transaction type must be Send and its own transaction type (Transaction::Transaction = Transaction). Implementations delegating to DatabaseConnection / DatabaseTransaction, and virtually all #[async_trait] implementations, already satisfy this. Callers are unaffected.

Upgrades

2026-07-20 08:11:12
sea-orm

sea-orm-cli@2.0.0

Reformat example manifests with taplo after 2.0.0 bump

2026-07-20 06:15:27
sea-orm

2.0.0

SeaORM 2.0.0

SeaORM 2.0 is the first stable release of the 2.x line. It reworks how entities, relations, and ActiveModels are defined and used, adds an entity-first workflow, introduces role-based access control, and brings the library onto SeaQuery 1.0 and SQLx 0.9.

The full, itemized changelog for the 2.0 series (all release candidates included) is in CHANGELOG.md.

Highlights

New entity format

Relations are now declared directly on the Model struct with #[sea_orm::model], replacing the separate Relation enum and Related impls.

#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "user")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
    #[sea_orm(has_one)]
    pub profile: HasOne<super::profile::Entity>,
    #[sea_orm(has_many)]
    pub posts: HasMany<super::post::Entity>,
}

See the new entity format walk-through.

BelongsTo relation type with compile-time cardinality

A belongs_to relation can be typed BelongsTo<Entity> (required) or BelongsTo<Option<Entity>> (optional), encoding the foreign-key cardinality in the type and paired with the write-side ActiveBelongsTo. The macro validates the type against the nullability of the from columns at compile time. BelongsTo is the recommended type for belongs_to; the legacy HasOne<Entity> field type remains supported for backward compatibility. (https://github.com/SeaQL/sea-orm/pull/3118)

Strongly-typed columns

Filter with the typed COLUMN constant for compile-time type safety, alongside the existing Column enum.

user::Entity::find().filter(user::COLUMN.name.contains("Bob"))

See strongly-typed columns.

Nested ActiveModel

Build and persist an entity together with its related rows in one expression, and push has-many children onto a loaded model.

let bob = user::ActiveModel::builder()
    .set_name("Bob")
    .set_email("bob@sea-ql.org")
    .set_profile(profile::ActiveModel::builder().set_picture("Tennis"))
    .insert(db)
    .await?;

See nested ActiveModel.

Entity Loader

Load an entity together with its relations, including nested relations, in a single call.

let user = user::Entity::load()
    .filter_by_id(12)
    .with(profile::Entity)
    .with((post::Entity, comment::Entity))
    .one(db)
    .await?;

Entity-first workflow

Create tables directly from entity definitions via the schema registry, without writing a migration first.

db.get_schema_registry("my_crate::*").sync(db).await?;

See the entity-first workflow.

Role-Based Access Control

A table-scoped, hierarchical RBAC engine with a query auditor and a RestrictedConnection that implements ConnectionTrait and enforces permissions on all Entity operations (including complex joins, insert-select, and CTE queries). (https://github.com/SeaQL/sea-orm/pull/2683)

Overhauled insert_many

insert_many no longer shares a helper struct with single insert. Panic-prone APIs were removed, empty input returns None / vec![] on exec, and the new InsertMany helper exposes last_insert_id: Option<Value>. (https://github.com/SeaQL/sea-orm/pull/2628)

Synchronous SeaORM

The sea-orm-sync crate provides a synchronous SeaORM backed by rusqlite, mirroring the async API with async/await stripped away.

Upgrading from 1.x

Follow the 1.0 to 2.0 migration guide and the 2.0 walk-through.

Notable breaking changes to be aware of:

  • Expression methods like .eq(), .like(), .contains() now require use sea_orm::ExprTrait; in scope. Also read SeaQuery's breaking changes.
  • execute / query_one / query_all / stream now take a SeaQuery statement; the raw-SQL variants are execute_raw / query_one_raw / query_all_raw / stream_raw.
  • PostgreSQL auto-increment columns now use GENERATED BY DEFAULT AS IDENTITY instead of serial; opt back in with option-postgres-use-serial if needed.
  • SQLite maps both Integer and BigInteger to integer.
  • DeriveValueType now also derives NotU8, IntoActiveValue, and TryFromU64; remove any manual implementations to avoid conflicts.
  • Removed the runtime-actix feature alias (use runtime-tokio); removed DeriveCustomColumn and default_as_str.

Dependencies

  • SeaQuery 1.0
  • SQLx 0.9
  • sea-schema 0.18
2026-07-15 05:53:51
sea-orm

2.0.0-rc.43

Release Notes: SeaORM 2.0.0-rc.43

(since 2.0.0-rc.42)

New Features

BelongsTo relation type (#3118, #3133, #3134)

A dedicated BelongsTo<E> / BelongsTo<Option<E>> type for belongs_to relations, alongside the existing HasOne. Cardinality lives in the type parameter, so the foreign-key nullability is expressed — and checked — at compile time:

  • BelongsTo<E> — the FK is NOT NULL; the relation cannot be detached (there is no way to set it to "none" on the active side), so orphaning a required parent is a compile error rather than a runtime constraint violation.
  • BelongsTo<Option<E>> — the FK is nullable; the relation can be detached, which nulls the FK on save.

The cardinality is validated against the FK columns when the entity is derived: BelongsTo<Entity> requires every from column to be NOT NULL, and BelongsTo<Option<Entity>> requires at least one nullable from column — a mismatch is a compile error. Composite foreign keys with mixed nullability detach by nulling only their nullable columns.

#[sea_orm(belongs_to, from = "user_id", to = "id")]
pub author: BelongsTo<super::user::Entity>,          // required

#[sea_orm(belongs_to, from = "bakery_id", to = "id")]
pub bakery: BelongsTo<Option<super::bakery::Entity>>, // optional

The active side is ActiveBelongsTo<..>, mirroring ActiveHasOne / ActiveHasMany.

HasOne still works for belongs_to — no migration required (#3133)

Adopting BelongsTo is opt-in. A belongs_to field may keep its existing HasOne<Entity> type; it continues to compile and behave as before. Use BelongsTo when you want the compile-time cardinality guarantee; otherwise nothing changes.

Enhancements

CLI lists valid options on generation error (#3131)

sea-orm-cli generate entity now prints the valid choices when an invalid option value is supplied, instead of only reporting that the value was rejected.

Bug Fixes

has_related with a Condition::any() filter (#3126)

has_related wrapped the caller's condition and then added the mandatory FK/join condition to it. When the caller passed a Condition::any() (an OR group), the join condition was folded into that disjunction, so the relation constraint was no longer guaranteed. The caller's condition is now wrapped in Condition::all() first, yielding (caller condition) AND (fk join) regardless of any() / all().

Entity codegen for PostgreSQL enum array columns (#3120)

Array-of-enum columns were generated as scalar active-enum fields because the type resolver unwrapped Array(Enum) to Enum. Array columns now route through the full type resolver, so enum[] columns generate Vec<Enum> fields.

Compatibility Notes

  • belongs_to relations may now be typed BelongsTo<Entity> / BelongsTo<Option<Entity>>. This is opt-in — existing HasOne-typed belongs_to fields are unchanged and still supported. sea-orm-cli continues to generate the HasOne form.
  • The compile-time detach guarantee applies only to BelongsTo<Entity> (non-null); it is a property of the type, so it costs nothing at runtime.
  • A BelongsTo field's type parameter must match its FK nullability (checked when the entity is derived). This only affects code that opts into BelongsTo.
  • The nested-ActiveModel relation types remain semver-exempt (unstable): rc.43 drops their PartialEq<Option<..>> impls, so compare an empty relation with is_unloaded_or_not_found() / is_not_found() / as_ref() rather than == None / == Some(..).
2026-07-05 02:56:59
sea-orm

2.0.0-rc.42

Release Notes: SeaORM 2.0.0-rc.42

(since 2.0.0-rc.41)

New Features

Typed value arrays via try_getable_array (#3108, #2967)

DeriveValueType wrappers backed by a Vec<_> now round-trip as native PostgreSQL arrays: the derive generates a try_getable_array implementation, so a newtype over Vec<i32> reads and writes as INTEGER[] without a manual TryGetable impl.

#[derive(Clone, Debug, PartialEq, DeriveValueType)]
pub struct Tags(Vec<String>);

Replace and delete a nested HasOne (#3110, #3060, #3061)

The active has-one type (ActiveHasOne) gains a Delete variant plus generated delete_<field> / set_<field>_option builders. Setting a populated has-one now replaces the existing linked record (deleting or orphaning the old one) instead of erroring, and Delete removes it on save.

let mut user = user::Entity::load().filter_by_id(1).with(profile::Entity).one(db).await?.unwrap();
user.delete_profile().save(db).await?; // remove the linked profile

TryFrom<&str> for active enums (#3111)

DeriveActiveEnum now generates a TryFrom<&str> implementation, so a string value can be parsed straight into the enum by its database string representation.

let color = Color::try_from("Black")?;

Enhancements

ActiveHasOne / ActiveHasMany (renamed)

The active/write-side companions to HasOne / HasMany are renamed from HasOneModel / HasManyModel to ActiveHasOne / ActiveHasMany, so the names read as active-model values rather than loaded models. The read-side HasOne / HasMany types are unchanged.

Documented SqlErr and DbErr::sql_err() (#2940)

DbErr::sql_err() and the SqlErr variants are documented, including how to reach the raw driver error for backend-specific handling.

if let Some(SqlErr::UniqueConstraintViolation(_)) = err.sql_err() { /* ... */ }

Schema sync warns on column-type divergence (#3106)

SchemaBuilder::sync() logs a warning when a live column's type diverges from the entity definition instead of silently ignoring the difference.

Bug Fixes

Codegen ColumnType coverage (#3092)

Entity generation emits compiling code for Year, Bit, VarBit, MacAddr, LTree, and Interval columns, and for Money columns carrying precision and scale — previously these produced non-compiling ColumnType expressions.

Skip generated columns in entity generation (#3094)

sea-orm-cli generate entity skips database-generated columns, which cannot be inserted or updated, instead of emitting them as ordinary fields.

SchemaBuilder::sync() returns a Send future (#3100)

Regression fix: sync() no longer returns a non-Send future, so it can be used across .await points on multi-threaded runtimes. Released together with sea-schema 0.18.1.

sea-orm-sync generation fixes (#3112)

The make-sync transform correctly handles futures_util usage in the mock driver, keeping the blocking sea-orm-sync crate in sync with the async source.

Compatibility Notes

  • HasOneModel / HasManyModel are renamed to ActiveHasOne / ActiveHasMany. Update references; the read-side HasOne / HasMany are unaffected.
  • DbErr, UpdateResult, DeleteResult, ActiveHasOne, and ActiveHasMany are now #[non_exhaustive]; downstream matches need a wildcard arm.
  • Schema sync and nested-ActiveModel relation mutation are marked unstable (semver-exempt) while their APIs settle.
  • Prebuilt sea-orm-cli binaries no longer include the Intel macOS (x86_64-apple-darwin) target.