NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #1967 most downloaded on crates.io
🐚 An async & dynamic ORM for Rust
Last release 10 days ago
27 Sep 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 57 of the last 60 stable releases
4 versions withdrawn
withdrawn after publishing
5 years old
142 releases · first in 2021
One column per quarter.
Add select_except to select all columns except the given ones #3211
(since 2.0.3)
Add select_except to select all columns except the given ones #3211
select_except provides the inverse of select_only / column——selecting all columns except the specified ones.
These methods replace the current selection, so the last one called takes effect.
#[derive(DerivePartialModel)]
#[sea_orm(entity = "post::Entity")]
struct PostPreview {
id: i32,
title: String,
}
let posts = post::Entity::find()
.select_except([post::Column::Body])
.into_model::<PostPreview>()
.all(db)
.await?;Fix chained left_join_linked using the wrong alias for links with multiple relations #3199
When left_join_linked was called more than once, later joins in a multi-relation link could refer to aliases belonging to the first call. The query remained valid but could return incorrect rows.
cake::Entity::find()
.left_join_linked(CakeToFilling)
.left_join_linked(CakeToFillingVendor)-- before
LEFT JOIN `filling` AS `r3` ON `r0`.`filling_id` = `r3`.`id`
-- after
LEFT JOIN `filling` AS `r3` ON `r2`.`filling_id` = `r3`.`id`Respect condition_type in linked joins #3203
left_join_linked and find_linked always combined the relation condition and on_condition with AND, even when ConditionType::Any was specified.
They now follow condition_type, matching regular join().
cake::Relation::Fruit
.def()
.condition_type(ConditionType::Any)
.on_condition(|_left, right| {
Expr::col((right, fruit::Column::Name))
.like("%tropical%")
.into_condition()
})-- before
LEFT JOIN `fruit` AS `r0` ON `cake`.`id` = `r0`.`cake_id` AND `r0`.`name` LIKE '%tropical%'
-- after
LEFT JOIN `fruit` AS `r0` ON `cake`.`id` = `r0`.`cake_id` OR `r0`.`name` LIKE '%tropical%'Fix save_as not being applied to array values in eq_any / ne_all #3208
#[sea_orm(column_type = r#"custom("citext")"#, save_as = "citext")]
pub email: String,
Entity::find().filter(Column::Email.eq_any(["a@x.com".to_owned(), "B@x.com".to_owned()]))-- before: text[], case-sensitive
WHERE "user"."email" = ANY(ARRAY ['a@x.com','B@x.com'])
-- after
WHERE "user"."email" = ANY(CAST(ARRAY ['a@x.com','B@x.com'] AS citext[]))Fix schema sync failing to drop a stale unique index on MySQL #3202
Removing #[sea_orm(unique)] from a column could make the next schema sync fail with MySQL error 1064.
Upgrade arrow to 60 (sea-orm-arrow 2.0.0-rc.5)
with-arrow now builds against arrow 60. Arrow types passed to SeaORM, such as a RecordBatch for from_arrow, must come from arrow 60 too, so crates like parquet need the matching version.
arrow = "60"
parquet = "60"select_except, save_as casts for eq_any / ne_all, linked-join alias and condition_type fixes, MySQL schema-sync index drop fix, arrow 60
Fix panics when using select_also / select_both with a column that requires a cast on select #3194
(since 2.0.2)
Fix panics when using select_also / select_both with a column that requires a cast on select #3194
A combined select derives a prefixed alias for each selected expression. However, a cast expression is neither a plain column nor an enum cast, so previously its alias could not be derived, causing a panic.
Columns now carry their own aliases into the combined select, with the prefix applied on top.
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", ...Use Span::mixed_site for generated identifiers where possible to fix name shadowing issues in most cases #3185
// did not compile before: `row` collided with a generated binding
#[derive(FromQueryResult)]
struct QueryResultProjection {
row: String,
}Fix DeriveActiveEnum conflicting with an enum variant named Error #3185
The generated TryFrom<&str> implementation used Self::Error in the return type of try_from. If the enum had a variant named Error, Self::Error would resolve to that variant instead of the associated type.
The implementation now explicitly uses sea_orm::DbErr.
#[derive(DeriveActiveEnum)]
enum ErrorVariantEnum {
Error, // no longer breaks the generated `TryFrom<&str>`
}Avoid filtering out an explicitly requested Eq derive on ModelEx #3193
Eq is deliberately not inherited from the model because a nested relation may contain fields that do not implement Eq.
However, the previous derive filtering logic also discarded an Eq explicitly requested by the user.
Explicit derives are now preserved, and the clippy::derive_partial_eq_without_eq warning caused by the missing Eq is suppressed.
#[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 EqSkip code generation for partial indexes #3196
Partial indexes are not yet supported, so they are now skipped during code generation to avoid generating incorrect constraints.
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)]Support postgres:db_name connection URLs #3197
sea-orm-cli generate entity -u postgres:my_db -o src/entities#3194 also changes the generated SQL for regular queries.
Assertions on the generated SQL need to be updated.
-- 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"select_as aliases in combined selects, derive macro hygiene, ModelEx Eq handling, PostgreSQL partial-unique-index and postgres:db_name CLI fixes
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::Recor
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 #3164date_time_default_now schema helper — a column defaulting to Expr::current_timestamp() #3159timestamp_default_now and timestamp_with_time_zone_default_now schema helpers, mirroring date_time_default_now for the timestamp family #3165--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 #3163require_one query helper, date_time_default_now / timestamp_default_now schema helpers, entity-merge duplicate-import fix
Add set_page to Paginator to set the current page #2963
set_page to Paginator to set the current page #2963as_option / into_option to ActiveValue<Option<V>>, flattening the outer active-value state and the inner option #3155set_unset and friends to ActiveValue: set the value only when currently NotSet #3083is_set_and / is_unchanged_and to ActiveValue #3125ConnectOptions::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 #3143MigratorTrait::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 #3144Require TransactionTrait::Transaction to be a fixed point, fixing nested-transaction recursion (E0275 / future_not_send) in #[sea_orm::model] generated save methods #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.
loco-rs 1.0 (which runs on SeaORM 2.0 stable) #3152ActiveValue helpers (set_unset, is_set_and, as_option), Paginator::set_page, before_acquire pool hooks, read-only migration status queries, nested-transaction recursion fix
Notable breaking changes to be aware of:
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.
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 cardinalityA 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. (#3118)
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"))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.
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?;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.
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).
(#2683)
insert_manyinsert_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>. (#2628)
The sea-orm-sync crate provides a synchronous SeaORM backed by rusqlite, mirroring
the async API with async/await stripped away.
Follow the 1.0 to 2.0 migration guide
and the 2.0 walk-through.
Notable breaking changes to be aware of:
.eq(), .like(), .contains() now requireuse sea_orm::ExprTrait; in scope. Also readexecute / query_one / query_all / stream now take a SeaQuery statement; theexecute_raw / query_one_raw / query_all_raw / stream_raw.GENERATED BY DEFAULT AS IDENTITY insteadserial; opt back in with option-postgres-use-serial if needed.Integer and BigInteger to integer.DeriveValueType now also derives NotU8, IntoActiveValue, and TryFromU64;runtime-actix feature alias (use runtime-tokio); removedDeriveCustomColumn and default_as_str.BelongsTo relation type (opt-in, compile-time FK cardinality), CLI generation-option errors, has_related Condition::any() & PG enum-array codegen fixesHasOne replace/delete, ActiveHasOne/ActiveHasMany rename, codegen ColumnType fixesSelectFourMany, update_without_returning, cargo binstall sea-orm-cli, junction ActiveModelBehavior & schema-sync PG-schema fixesfind_both_related, set_ne, pool options, schema sync fixestry_from_u64 for DeriveValueTypeMigratorTrait with self, PostgreSQL application_namene_all, typed TextUuid, COUNT overflow fixsea-query bumpsqlx-all in migration, set_if_not_equals_and, auto_increment for String/Uuid PKsDeriveValueType implements NotU8 for PostgreSQL arrayspostgres-use-serial-pk feature for legacy serial PKssea-query bumpsea-query bump to rc.27DeriveValueType implements IntoActiveValue, remove NotU8DatabaseExecutor unified type, value array refactorsea-orm-sync crate, exists on PaginatorTraitSplit belongs_to from has_one with a new BelongsTo relation type https://github.com/SeaQL/sea-orm/pull/3118
A belongs_to relation can now be typed BelongsTo<Entity> (required) or
BelongsTo<Option<Entity>> (optional), encoding the foreign-key cardinality in the type,
paired with the write-side companion ActiveBelongsTo. BelongsTo is the recommended
type for belongs_to; the legacy HasOne<Entity> field type remains supported for
backward compatibility.
Role Based Access Control https://github.com/SeaQL/sea-orm/pull/2683
* to grant all permissions or resourcesRestrictedConnection.
it implements ConnectionTrait, and will audit all queries and perform permission check,
and reject them accordingly. all Entity operations except raw SQL are supported.
complex joins, insert select from, and even CTE queries are supported.// load rules from database
db_conn.load_rbac().await?;
// admin can create bakery
let db = db_conn.restricted_for(admin)?;
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
..Default::default()
};
assert!(Bakery::insert(seaside_bakery).exec(&db).await.is_ok());
// public cannot create bakery
let db = db_conn.restricted_for(public)?;
assert!(matches!(
Bakery::insert(bakery::ActiveModel::default())
.exec(&db)
.await,
Err(DbErr::AccessDenied { .. })
));
Entity::insert_many. We've made a number of changes https://github.com/SeaQL/sea-orm/pull/2628
InsertMany, last_insert_id is now Option<Value>None or vec![] is returned on exec operationsTryInsert API is unchangedPreviously, insert_many shares the same helper struct with insert_one, which led to an awkard API.
let res = Bakery::insert_many(std::iter::empty())
.on_empty_do_nothing() // <- you need to add this
.exec(db)
.await;
assert!(matches!(res, Ok(TryInsertResult::Empty)));
last_insert_id is now Option<Value>:
struct InsertManyResult<A: ActiveModelTrait>
{
pub last_insert_id: Option<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>,
}
Which means the awkardness is removed:
let res = Entity::insert_many::<ActiveModel, _>([]).exec(db).await;
assert_eq!(res?.last_insert_id, None); // insert nothing return None
let res = Entity::insert_many([ActiveModel { id: Set(1) }, ActiveModel { id: Set(2) }])
.exec(db)
.await;
assert_eq!(res?.last_insert_id, Some(2)); // insert something return Some
Same on conflict API as before:
let res = Entity::insert_many([ActiveModel { id: Set(3) }, ActiveModel { id: Set(4) }])
.on_conflict_do_nothing()
.exec(db)
.await;
assert!(matches!(conflict_insert, Ok(TryInsertResult::Conflicted)));
Exec with returning now returns a Vec<Model>, so it feels intuitive:
assert!(
Entity::insert_many::<ActiveModel, _>([])
.exec_with_returning(db)
.await?
.is_empty() // no footgun, nice
);
assert_eq!(
Entity::insert_many([
ActiveModel {
id: NotSet,
value: Set("two".into()),
}
])
.exec_with_returning(db)
.await
.unwrap(),
[
Model {
id: 2,
value: "two".into(),
}
]
);
ActiveModel::from_json. Consider the following Entity https://github.com/SeaQL/sea-orm/pull/2599#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32, // <- not nullable
pub name: String,
}
Previously, the following would result in error "missing field id":
assert!(
cake::ActiveModel::from_json(json!({
"name": "Apple Pie",
})).is_err();
);
Now, the ActiveModel will be partially filled:
assert_eq!(
cake::ActiveModel::from_json(json!({
"name": "Apple Pie",
}))
.unwrap(),
cake::ActiveModel {
id: NotSet,
name: Set("Apple Pie".to_owned()),
}
);
Model can now be used as PartialModel in nested query https://github.com/SeaQL/sea-orm/pull/2642#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct Cake {
id: i32,
name: String,
#[sea_orm(nested)]
bakery: Option<bakery::Model>,
}
let cake: Cake = cake::Entity::find()
.left_join(bakery::Entity)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.await?
.unwrap();
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert_eq!(
cake.bakery.unwrap(),
bakery::Model {
id: 42,
name: "cool little bakery".to_string(),
}
);
DeriveValueType can now be used as primary key https://github.com/SeaQL/sea-orm/pull/2643#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "my_value_type")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: MyInteger,
}
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
pub struct MyInteger(pub i32);
// only for i8 | i16 | i32 | i64 | u8 | u16 | u32 | u64
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "lineitem")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique_key = "item")]
pub order_id: i32,
#[sea_orm(unique_key = "item")]
pub cake_id: i32,
}
let stmts = Schema::new(backend).create_index_from_entity(lineitem::Entity);
assert_eq!(
stmts[0],
Index::create()
.name("idx-lineitem-item")
.table(lineitem::Entity)
.col(lineitem::Column::OrderId)
.col(lineitem::Column::CakeId)
.unique()
.take()
);
assert_eq!(
backend.build(stmts[0]),
r#"CREATE UNIQUE INDEX "idx-lineitem-item" ON "lineitem" ("order_id", "cake_id")"#
);
ConnectionTrait API: execute, query_one, query_all, stream now takes in SeaQuery statement instead of raw SQL statement https://github.com/SeaQL/sea-orm/pull/2657// old
let query: SelectStatement = Entity::find().filter(..).into_query();
let backend = self.db.get_database_backend();
let stmt = backend.build(&query);
let rows = self.db.query_all(stmt).await?;
// new
let query: SelectStatement = Entity::find().filter(..).into_query();
let rows = self.db.query_all(&query).await?;
raw_sql macro for ergonomic parameter injection#[derive(FromQueryResult)]
struct Cake {
name: String,
#[sea_orm(nested)]
bakery: Option<Bakery>,
}
#[derive(FromQueryResult)]
struct Bakery {
#[sea_orm(alias = "bakery_name")]
name: String,
}
let cake_ids = [2, 3, 4]; // expanded by the `..` operator
let cake: Option<Cake> = Cake::find_by_statement(raw_sql!(
Sqlite,
r#"SELECT "cake"."name", "bakery"."name" AS "bakery_name"
FROM "cake"
LEFT JOIN "bakery" ON "cake"."bakery_id" = "bakery"."id"
WHERE "cake"."id" IN ({..cake_ids})"#
))
.one(db)
.await?;
consolidate method to SelectThree. This output has different shape depending on the topology of the join.// Order -> Customer
// -> Lineitem
let items: Vec<(order::Model, Option<customer::Model>, Option<lineitem::Model>)> =
order::Entity::find()
.find_also_related(customer::Entity)
.find_also_related(lineitem::Entity)
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.all(&ctx.db)
.await?;
// flat result
assert_eq!(
items,
vec![
(order, Some(customer), Some(line_1)),
(order, Some(customer), Some(line_2)),
]
);
let items: Vec<(order::Model, Vec<customer::Model>, Vec<lineitem::Model>)> =
order::Entity::find()
.find_also_related(customer::Entity)
.find_also_related(lineitem::Entity)
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.consolidate() // <-
.all(&ctx.db)
.await?;
// consolidated by order
assert_eq!(
items,
vec![(
order,
vec![customer],
vec![line_1, line_2]
)]
);
// Order -> Lineitem -> Cake
let items: Vec<(order::Model, Option<lineitem::Model>, Option<cake::Model>)> =
order::Entity::find()
.find_also_related(lineitem::Entity)
.and_also_related(cake::Entity)
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.all(&ctx.db)
.await?;
// flat result
assert_eq!(
items,
vec![
(order, Some(line_1), Some(cake_1)),
(order, Some(line_2), Some(cake_2)),
]
);
let items: Vec<(order::Model, Vec<(lineitem::Model, Vec<cake::Model>)>)> =
order::Entity::find()
.find_also_related(lineitem::Entity)
.and_also_related(cake::Entity)
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.consolidate() // <-
.all(&ctx.db)
.await?;
// consolidated by order first, then by line
assert_eq!(
items,
vec![(
order,
vec![(line_1, vec![cake_1]), (line_2, vec![cake_2])]
)]
);
Select::has_related// cake -> fruit: find all cakes containing mango
assert_eq!(
cake::Entity::find()
.has_related(fruit::Entity, fruit::Column::Name.eq("Mango"))
.build(DbBackend::Sqlite)
.to_string(),
[
r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
r#"WHERE EXISTS(SELECT 1 FROM "fruit""#,
r#"WHERE "fruit"."name" = 'Mango'"#,
r#"AND "cake"."id" = "fruit"."cake_id")"#,
]
.join(" ")
);
// cake -> cake_filling -> filling: find all cakes with orange fillings
assert_eq!(
cake::Entity::find()
.has_related(filling::Entity, filling::Column::Name.eq("Marmalade"))
.build(DbBackend::Sqlite)
.to_string(),
[
r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
r#"WHERE EXISTS(SELECT 1 FROM "filling""#,
r#"INNER JOIN "cake_filling" ON "cake_filling"."filling_id" = "filling"."id""#,
r#"WHERE "filling"."name" = 'Marmalade'"#,
r#"AND "cake"."id" = "cake_filling"."cake_id")"#,
]
.join(" ")
);
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "staff")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub reports_to_id: Option<i32>,
#[sea_orm(self_ref, relation_enum = "ReportsTo", from = "reports_to_id", to = "id")]
pub reports_to: HasOne<Entity>,
}
// Entity Loader
let staff = staff::Entity::load()
.with(staff::Relation::ReportsTo)
.all(db)
.await?;
assert_eq!(staff[0].name, "Alan");
assert_eq!(staff[0].reports_to, None);
assert_eq!(staff[1].name, "Ben");
assert_eq!(staff[1].reports_to.as_ref().unwrap().name, "Alan");
assert_eq!(staff[2].name, "Alice");
assert_eq!(staff[2].reports_to.as_ref().unwrap().name, "Alan");
// Model Loader
let staff = staff::Entity::find().all(db).await?;
let reports_to = staff
.load_self(staff::Entity, staff::Relation::ReportsTo, db)
.await?;
assert_eq!(staff[0].name, "Alan");
assert_eq!(reports_to[0], None);
assert_eq!(staff[1].name, "Ben");
assert_eq!(reports_to[1].unwrap().name, "Alan");
assert_eq!(staff[2].name, "Alice");
assert_eq!(reports_to[2].unwrap().name, "Alan");
// old
user::Entity::find().filter(user::Column::Name.contains("Bob"))
// new
user::Entity::find().filter(user::COLUMN.name.contains("Bob"))
// compile error: the trait `From<{integer}>` is not implemented for `String`
user::Entity::find().filter(user::COLUMN.name.like(2))
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "access_log")]
pub struct Model {
.. // with `chrono` crate
pub ts: ChronoUnixTimestamp,
pub ms: ChronoUnixTimestampMillis,
.. // with `time` crate
pub ts: TimeUnixTimestamp,
pub ms: TimeUnixTimestampMillis,
}
Nested ActiveModel (ActiveModelEx) and cascade operations https://github.com/SeaQL/sea-orm/pull/2818
The following operation saves a new set of user + profile + post + tag + post_tag into the database atomically:
let user = user::ActiveModel::builder()
.set_name("Bob")
.set_email("bob@sea-ql.org")
.set_profile(profile::ActiveModel::builder().set_picture("image.jpg"))
.add_post(
post::ActiveModel::builder()
.set_title("Nice weather")
.add_tag(tag::ActiveModel::builder().set_tag("sunny")),
)
.save(db)
.await?;
serde featureTextUuid now derives Serialize and Deserialize when the serde feature is enabled--column-extra-derives https://github.com/SeaQL/sea-orm/pull/2212--big-integer-type=i32 to use i32 for bigint (for SQLite)--experimental-preserve-user-modifications https://github.com/SeaQL/sea-orm/pull/2755 https://github.com/SeaQL/sea-orm/pull/2964Model::try_setBackendNotSupported. Previously, it panics with e.g. "Database backend doesn't support RETURNING" https://github.com/SeaQL/sea-orm/pull/2630let result = cake::Entity::insert_many([])
.exec_with_returning_keys(db)
.await;
if db.support_returning() {
// Postgres and SQLite
assert_eq!(result.unwrap(), []);
} else {
// MySQL
assert!(matches!(result, Err(DbErr::BackendNotSupported { .. })));
}
PrimaryKeyNotSet. Previously, it panics with "PrimaryKey is not set" https://github.com/SeaQL/sea-orm/pull/2627assert!(matches!(
Update::one(cake::ActiveModel {
..Default::default()
})
.exec(&db)
.await,
Err(DbErr::PrimaryKeyNotSet { .. })
));
Schema::create_enum_from_active_enum https://github.com/SeaQL/sea-orm/pull/2634fn create_enum_from_active_enum<A>(&self) -> Option<TypeCreateStatement>
// method can now return None
ColumnTrait::eq_any as a shorthand for the = ANY operator. Postgres only.assert_eq!(
cake::Entity::find()
.filter(cake::Column::Id.eq_any(vec![4, 5]))
.build(DbBackend::Postgres)
.to_string(),
r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = ANY(ARRAY [4,5])"#
);
ActiveModelTrait::try_setpub trait ActiveModelTrait {
/// old: set the Value of a ActiveModel field, panic if failed
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value) {
self.try_set(c, v).unwrap_or_else(|e| panic!(..))
}
/// new: same as above but non-panicking
fn try_set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value) -> Result<(), DbErr>;
}
Linked can now be used in partial select, in case Related cannot be definedpub struct ToBakery;
impl Linked for ToBakery {
type FromEntity = super::cake::Entity;
type ToEntity = super::bakery::Entity;
fn link(&self) -> Vec<RelationDef> {
vec![Relation::Bakery.def()]
}
}
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct Cake2 {
id: i32,
name: String,
#[sea_orm(nested, alias = "r0")]
bakery: Option<Bakery>,
#[sea_orm(skip)]
ignore: Ignore,
}
let cake2: Cake2 = cake::Entity::find()
.left_join_linked(ToBakery)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.await?
.unwrap();
RelationDef now implements Clone. on_condition is changed to Arc but this is a minor breaking change.extra on column attribute:#[cfg(feature = "with-rust_decimal")]
#[sea_orm(extra = "CHECK (price > 0)")]
pub price: Decimal,
// results in:
ColumnDef::new("price")
.decimal()
.not_null()
.extra("CHECK (price > 0)"),
ColumnTrait::avg, in addition to sum, min, max etclet average: Decimal = order::Entity::find()
.select_only()
.column_as(order::Column::Total.avg(), "avg")
.into_tuple()
.one(&ctx.db)
.await?
.unwrap();
SchemaBuilder::sync can now be used in migrations#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
db.get_schema_builder()
.register(note::Entity)
.sync(db)
.await
}
}
max_lifetime and idle_timeout Parameters https://github.com/SeaQL/sea-orm/pull/2748u32 in Postgres as i32 https://github.com/SeaQL/sea-orm/pull/2753DeriveActiveEnum now also impl IntoActiveValue https://github.com/SeaQL/sea-orm/issues/1972DeriveValueType now also supports any structs that can be converted to / from string https://github.com/SeaQL/sea-orm/issues/2811#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub struct Tag3 {
pub i: i64,
}
impl std::fmt::Display for Tag3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.i)
}
}
impl std::str::FromStr for Tag3 {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let i: i64 = s.parse()?;
Ok(Self { i })
}
}
DeriveIntoActiveModel on Option<T> fields https://github.com/SeaQL/sea-orm/pull/2926#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct PartialFruit {
cake_id: Option<i32>,
}
assert_eq!(
PartialFruit { cake_id: Some(1) }.into_active_model(),
fruit::ActiveModel { id: NotSet, name: NotSet, cake_id: Set(Some(1)) }
);
assert_eq!(
PartialFruit { cake_id: None }.into_active_model(),
fruit::ActiveModel { id: NotSet, name: NotSet, cake_id: NotSet }
);
FromQueryResult now supports nullable nested model https://github.com/SeaQL/sea-orm/pull/2845#[derive(FromQueryResult)]
struct CakeWithOptionalBakeryModel {
#[sea_orm(alias = "cake_id")]
id: i32,
#[sea_orm(alias = "cake_name")]
name: String,
#[sea_orm(nested)]
bakery: Option<bakery::Model>, // can be null
}
try_from_u64 to DeriveValueType https://github.com/SeaQL/sea-orm/pull/2958// Test for try_from_u64 attribute with type alias
type UserId = i32;
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(try_from_u64)]
pub struct MyUserId(pub UserId);
ArrowSchema, DeriveArrowSchemaHashMap and BTreeMap for JSON columns via TryGetableFromJson https://github.com/SeaQL/sea-orm/pull/3009Entity, Column, PrimaryKey, and ActiveModel https://github.com/SeaQL/sea-orm/pull/3029drop_everything now drops custom types with CASCADEPlease read SeaQuery's breaking changes as well. But for most compile errors, you can simply add use sea_orm::ExprTrait; in scope.
error[E0599]: no method named `like` found for enum `sea_query::Expr` in the current scope
|
| Expr::col((self.entity_name(), *self)).like(s)
|
| fn like<L>(self, like: L) -> Expr
| ---- the method is available for `sea_query::Expr` here
|
= help: items from traits can only be used if the trait is in scope
help: trait `ExprTrait` which provides `like` is implemented but not in scope; perhaps you want to import it
|
-> + use sea_orm::ExprTrait;
error[E0308]: mismatched types
--> src/sqlite/discovery.rs:27:57
|
| .and_where(Expr::col(Alias::new("type")).eq("table"))
| -- ^^^^^^^ expected `&Expr`, found `&str`
| |
| arguments to this method are incorrect
|
= note: expected reference `&sea_query::Expr`
found reference `&'static str`
error[E0308]: mismatched types
|
390 | Some(Expr::col(Name).eq(PgFunc::any(query.symbol)))
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&Expr`, found `FunctionCall`
| |
| arguments to this method are incorrect
|
note: method defined here
--> /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/cmp.rs:254:8
error[E0277]: the trait bound `sea_orm::Condition: From<bool>` is not satisfied
|
367 | .add_option(option)
| ---------- ^^^^^^ the trait `From<bool>` is not implemented for `sea_orm::Condition`
| |
| required by a bound introduced by this call
|
= note: required for `bool` to implement `Into<sea_orm::Condition>`
runtime-actix feature flag. It's been an alias of runtime-tokio for more than a year, so there should be no impact.sqlite-use-returning-for-3_35 by default. SQLite 3.35 was released in 2021, it should be the default by now.impl<T: ModelTrait + FromQueryResult> PartialModelTrait for T, there may be a potential conflict https://github.com/SeaQL/sea-orm/pull/2642DeriveValueType will also TryFromU64 if applicable, there may be a potential conflict https://github.com/SeaQL/sea-orm/pull/2643DeriveValueType also impl IntoActiveValue and NotU8, there may be a potential conflictTryIntoModel and Serialize to trait bounds of ActiveModel::from_json. There should be no impact if your models are derived with DeriveEntityModel https://github.com/SeaQL/sea-orm/pull/2599fn from_json(mut json: serde_json::Value) -> Result<Self, DbErr>
where
Self: TryIntoModel<<Self::Entity as EntityTrait>::Model>,
<<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
serde::de::Deserialize<'de> + serde::Serialize,
DerivePartialModel now implement FromQueryResult by default, so there may be a potential conflict. Remove FromQueryResult in these cases https://github.com/SeaQL/sea-orm/pull/2653error[E0119]: conflicting implementations of trait `sea_orm::FromQueryResult` for type `CakeWithFruit`
|
> | #[derive(DerivePartialModel, FromQueryResult)]
| ------------------ ^^^^^^^^^^^^^^^ conflicting implementation for `CakeWithFruit`
IdenStatic and EntityName definition https://github.com/SeaQL/sea-orm/pull/2667trait IdenStatic {
fn as_str(&self) -> &'static str; // added static lifetime
}
trait EntityName {
fn table_name(&self) -> &'static str; // added static lifetime
}
DeriveCustomColumn and default_as_str https://github.com/SeaQL/sea-orm/pull/2667// This is no longer supported:
#[derive(Copy, Clone, Debug, EnumIter, DeriveCustomColumn)]
pub enum Column {
Id,
Name,
}
impl IdenStatic for Column {
fn as_str(&self) -> &str {
match self {
Self::Name => "my_name",
_ => self.default_as_str(),
}
}
}
// Do the following instead:
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
#[sea_orm(column_name = "my_name")]
Name,
}
execute, query_one, query_all, stream now takes in SeaQuery statement instead of raw SQL statement. a new set of methods execute_raw, query_one_raw, query_all_raw, stream_raw is added https://github.com/SeaQL/sea-orm/pull/2657 --> src/executor/paginator.rs:53:38
|
> | let rows = self.db.query_all(stmt).await?;
| --------- ^^^^ expected `&_`, found `Statement`
| |
| arguments to this method are incorrect
|
= note: expected reference `&_`
found struct `statement::Statement`
let backend = self.db.get_database_backend();
let stmt = backend.build(&query);
// change to:
let rows = self.db.query_all_raw(stmt).await?;
// if the query is a SeaQuery statement, then just do this:
let rows = self.db.query_all(&query).await?; // no need to build query
DatabaseConnection is changed from enum to struct. The original enum is moved into DatabaseConnection::inner. The new enum is named DatabaseConnectionType https://github.com/SeaQL/sea-orm/pull/2671error[E0599]: no associated item named `Disconnected` found for struct `db_connection::DatabaseConnection` in the current scope
--> src/database/db_connection.rs:137:33
|
> | pub struct DatabaseConnection {
| ----------------------------- associated item `Disconnected` not found for this struct
...
> | DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
| ^^^^^^^^^^^^ associated item not found in `DatabaseConnection`
match conn.inner {
DatabaseConnectionType::Disconnected => (),
_ => (),
}
DeleteOne and UpdateOne no longer implement QueryFilter and QueryTrait
directly. Those implementations could expose an incomplete SQL query with an
incomplete condition that touches too many records. To generate the right
condition, we must make sure that the primary key is set on the input
ActiveModel. If you need to access the generated SQL query, convert into
ValidatedDeleteOne/ValidatedUpdateOne first.error[E0599]: no method named `build` found for struct `query::update::UpdateOne` in the current scope
--> src/entity/column.rs:607:22
|
> | / Update::one(active_model)
> | | .build(DbBackend::Postgres)
| | -^^^^^ method not found in `UpdateOne<A>`
| |_____________________|
|
Call the validate() method:
Update::one(active_model)
+ .validate()?
.build(DbBackend::Postgres)
DbBackend::get_query_builder() because QueryBuilder is not longer object safe. - fn get_query_builder(&self) -> Box<dyn QueryBuilder>
SelectTwoMany: into_partial_model, into_json, stream. These methods are same as those in SelectTwo.
Please use Cake::find().find_also_related(Fruit).into_json() instead.delete_by_id method has changed to returning DeleteOne instead of DeleteMany. It doesn't change normal exec usage, but would change return type of exec_with_returning to Option<Model>fn delete_by_id<T>(values: T) -> DeleteMany<Self> // old
fn delete_by_id<T>(values: T) -> ValidatedDeleteOne<Self> // new
DeriveActiveEnum now also automatically impl IntoActiveValue, if you have a custom impl before, there would be a collisionwith-bigdecimal is now removed from default featuresRuntimeErr::SqlxError is now held in Arc to make DbErr clonable and smaller:pub enum RuntimeErr {
SqlxError(Arc<sqlx::error::Error>),
strum to 0.27Release Notes: SeaORM 2.0.0-rc.43
(since 2.0.0-rc.42)
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 isBelongsTo<Option<E>> — the FK is nullable; the relation can be detached, whichThe 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>>, // optionalThe 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.
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.
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.
belongs_to relations may now be typed BelongsTo<Entity> /BelongsTo<Option<Entity>>. This is opt-in — existing HasOne-typed belongs_tosea-orm-cli continues to generate theHasOne form.BelongsTo<Entity> (non-null);BelongsTo field's type parameter must match its FK nullability (checked whenBelongsTo.ActiveModel relation types remain semver-exempt (unstable): rc.43PartialEq<Option<..>> impls, so compare an empty relation withis_unloaded_or_not_found() / is_not_found() / as_ref() rather than == None== Some(..).Release Notes: SeaORM 2.0.0-rc.42
(since 2.0.0-rc.41)
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 profileTryFrom<&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")?;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.
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.
HasOneModel / HasManyModel are renamed to ActiveHasOne / ActiveHasMany.HasOne / HasMany are unaffected.DbErr, UpdateResult, DeleteResult, ActiveHasOne, and ActiveHasMany are#[non_exhaustive]; downstream matches need a wildcard arm.sea-orm-cli binaries no longer include the Intel macOSx86_64-apple-darwin) target.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Add find_linked_recursive method to ModelTrait https://github.com/SeaQL/sea-orm/pull/2480
find_linked_recursive method to ModelTrait https://github.com/SeaQL/sea-orm/pull/2480from_sqlx_*_row_to_proxy_row functions https://github.com/SeaQL/sea-orm/pull/2744Nothing published for this version
Added map_sqlx_mysql_opts, map_sqlx_postgres_opts, map_sqlx_sqlite_opts to ConnectOptions https://github.com/SeaQL/sea-orm/pull/2731 `rust let mut opt
map_sqlx_mysql_opts, map_sqlx_postgres_opts, map_sqlx_sqlite_opts to ConnectOptions https://github.com/SeaQL/sea-orm/pull/2731let mut opt = ConnectOptions::new(url);
opt.map_sqlx_postgres_opts(|pg_opt: PgConnectOptions| {
pg_opt.ssl_mode(PgSslMode::Require)
});
mariadb-use-returning to use returning syntax for MariaDB https://github.com/SeaQL/sea-orm/pull/2710sea-orm-rocket 0.6 https://github.com/SeaQL/sea-orm/pull/2732Fix enum casting in DerivePartialModel https://github.com/SeaQL/sea-orm/pull/2719 https://github.com/SeaQL/sea-orm/pull/2720 ```rust
#[derive(DerivePartialModel)]
#[sea_orm(entity = "active_enum::Entity", from_query_result, alias = "zzz")]
struct PartialWithEnumAndAlias {
#[sea_orm(from_col = "tea")]
foo: Option<Tea>,
}
let sql = active_enum::Entity::find()
.into_partial_model::<PartialWithEnumAndAlias>()
.into_statement(DbBackend::Postgres)
.sql;
assert_eq!(
sql,
r#"SELECT CAST("zzz"."tea" AS "text") AS "foo" FROM "public"."active_enum""#,
);
Allow DerivePartialModel to have nested aliases https://github.com/SeaQL/sea-orm/pull/2686 ```rust
DerivePartialModel to have nested aliases https://github.com/SeaQL/sea-orm/pull/2686#[derive(DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity", from_query_result)]
struct Factory {
id: i32,
#[sea_orm(from_col = "name")]
plant: String,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", from_query_result)]
struct CakeFactory {
id: i32,
name: String,
#[sea_orm(nested, alias = "factory")] // <- new
bakery: Option<Factory>,
}
ActiveModelTrait::try_set https://github.com/SeaQL/sea-orm/pull/2706fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
/// New: a non-panicking version of above
fn try_set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value) -> Result<(), DbErr>;
[sea-orm-cli] Mask sensitive ENV values https://github.com/SeaQL/sea-orm/pull/2658
FromJsonQueryResult: panic on serialization failures https://github.com/SeaQL/sea-orm/pull/2635#[derive(Clone, Debug, PartialEq, Deserialize, FromJsonQueryResult)]
pub struct NonSerializableStruct;
impl Serialize for NonSerializableStruct {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Err(serde::ser::Error::custom(
"intentionally failing serialization",
))
}
}
let model = Model {
json: Some(NonSerializableStruct),
};
let _ = model.into_active_model().insert(&ctx.db).await; // panic here
[sea-orm-cli] New --frontend-format flag to generate entities in pure Rust https://github.com/SeaQL/sea-orm/pull/2631 ```rust // for example, below is
--frontend-format flag to generate entities in pure Rust https://github.com/SeaQL/sea-orm/pull/2631// for example, below is the normal (compact) Entity:
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}
// this is the generated frontend model, there is no SeaORM dependency:
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Model {
#[serde(skip_deserializing)]
pub id: i32,
pub name: Option<String> ,
}
Loader https://github.com/SeaQL/sea-orm/pull/2637Make sea-orm-cli & sea-orm-migration dependencies optional https://github.com/SeaQL/sea-orm/pull/2367
anyhow::Error https://github.com/SeaQL/sea-orm/pull/2602column_name in DeriveColumn Column::from_str impl https://github.com/SeaQL/sea-orm/pull/2603#[derive(DeriveEntityModel)]
pub struct Model {
#[sea_orm(column_name = "lAsTnAmE")]
last_name: String,
}
assert!(matches!(Column::from_str("lAsTnAmE").unwrap(), Column::LastName));
Added ActiveModelTrait::default_values `rust assert_eq!( fruit::ActiveModel::default_values(), fruit::ActiveModel { id: Set(0), name: Set("".into()),
ActiveModelTrait::default_valuesassert_eq!(
fruit::ActiveModel::default_values(),
fruit::ActiveModel {
id: Set(0),
name: Set("".into()),
cake_id: Set(None),
type_without_default: NotSet,
},
);
IntoCondition for RelationDef https://github.com/SeaQL/sea-orm/pull/2587// This allows using `RelationDef` directly where sea-query expects an `IntoCondition`
let query = Query::select()
.from(fruit::Entity)
.inner_join(cake::Entity, fruit::Relation::Cake.def())
.to_owned();
PgVector codegen https://github.com/SeaQL/sea-orm/pull/2589AsEnum casting https://github.com/SeaQL/sea-orm/pull/2570assert_eq!(
lunch_set::Entity::find()
.select_only()
.column(lunch_set::Column::Tea)
.build(DbBackend::Postgres)
.to_string(),
r#"SELECT CAST("lunch_set"."tea" AS "text") FROM "lunch_set""#
// "text" is now quoted; will work for "text"[] as well
);
heck to 0.5 https://github.com/SeaQL/sea-orm/pull/2218sea-query to 0.32.5sea-schema to 0.16.2Upgrade sqlx to 0.8.4 https://github.com/SeaQL/sea-orm/pull/2562
Your coding agent can read these notes before it upgrades. Set up the MCP server →