Files
Notes/notes-service/vendor/sea-orm/CHANGELOG.md
T
2026-08-01 16:11:49 +03:00

129 KiB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

2.0.0 - 2026-07-19

Release Candidates

  • 2.0.0-rc.43BelongsTo relation type (opt-in, compile-time FK cardinality), CLI generation-option errors, has_related Condition::any() & PG enum-array codegen fixes
  • 2.0.0-rc.42 — typed value arrays, HasOne replace/delete, ActiveHasOne/ActiveHasMany rename, codegen ColumnType fixes
  • 2.0.0-rc.41SelectFourMany, update_without_returning, cargo binstall sea-orm-cli, junction ActiveModelBehavior & schema-sync PG-schema fixes
  • 2.0.0-rc.40 - Restore pgvector binding with SQLx 0.9
  • 2.0.0-rc.39 - SeaQuery 1.0, SQLx 0.9, async transaction helpers
  • 2.0.0-rc.38find_both_related, set_ne, pool options, schema sync fixes
  • 2.0.0-rc.37 — ER Diagram Generation
  • 2.0.0-rc.36 — Per-migration transaction control
  • 2.0.0-rc.35 — SQLite transaction modes, DeriveIntoActiveModel extensions, Decimal64/Bytes, schema sync fix
  • 2.0.0-rc.34 — Arrow/Parquet support, try_from_u64 for DeriveValueType
  • 2.0.0-rc.32MigratorTrait with self, PostgreSQL application_name
  • 2.0.0-rc.31ne_all, typed TextUuid, COUNT overflow fix
  • 2.0.0-rc.30 — Maintenance release, sea-query bump
  • 2.0.0-rc.29 — Tracing spans, UUID-as-TEXT, relation filtering, LEFT JOIN fix
  • 2.0.0-rc.28sqlx-all in migration, set_if_not_equals_and, auto_increment for String/Uuid PKs
  • 2.0.0-rc.27DeriveValueType implements NotU8 for PostgreSQL arrays
  • 2.0.0-rc.26postgres-use-serial-pk feature for legacy serial PKs
  • 2.0.0-rc.25 — Value system restoration, sea-query bump
  • 2.0.0-rc.24sea-query bump to rc.27
  • 2.0.0-rc.23DeriveValueType implements IntoActiveValue, remove NotU8
  • 2.0.0-rc.22DatabaseExecutor unified type, value array refactor
  • 2.0.0-rc.21 — Rusqlite / sea-orm-sync crate, exists on PaginatorTrait
  • 2.0.0-rc.20 — Stringy newtypes, M2M self-ref, nullable columns, bug fixes

New Features

  • Split 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

    1. a hierarchical RBAC engine that is table scoped
      • a user has 1 (and only 1) role
      • a role has a set of permissions on a set of resources
        • permissions here are CRUD operations and resources are tables
        • but the engine is generic so can be used for other things
      • roles have hierarchy, and so can inherit permissions
      • there is a wildcard * to grant all permissions or resources
      • individual users can have rules override
    2. a set of Entities to load / store the access control rules to / from database
    3. a query auditor that dissect queries for necessary permissions (implemented in SeaQuery)
    4. integration of RBAC into SeaORM in form of RestrictedConnection. 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 { .. })
));
  • Overhauled Entity::insert_many. We've made a number of changes https://github.com/SeaQL/sea-orm/pull/2628
    1. removed APIs that can panic
    2. new helper struct InsertMany, last_insert_id is now Option<Value>
    3. on empty iterator, None or vec![] is returned on exec operations
    4. TryInsert API is unchanged

Previously, 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(),
        }
    ]
);
#[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()),
    }
);
#[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(),
    }
);
#[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")"#
);
// 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?;
  • Added 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?;
  • Added 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])]
    )]
);
  • Added 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(" ")
);
  • Support self-referencing relations in loader
#[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))
  • Unix timestamp column type that will be mapped to big integer in database
#[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?;

Enhancements

let 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 { .. })));
}
assert!(matches!(
    Update::one(cake::ActiveModel {
        ..Default::default()
    })
    .exec(&db)
    .await,
    Err(DbErr::PrimaryKeyNotSet { .. })
));
fn create_enum_from_active_enum<A>(&self) -> Option<TypeCreateStatement>
// method can now return None
  • Added 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])"#
);
  • Added ActiveModelTrait::try_set
pub 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 defined
pub 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.
  • Added 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)"),
  • Added ColumnTrait::avg, in addition to sum, min, max etc
let 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
    }
}
#[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 })
    }
}
#[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 }
);
#[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
}
// 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);

Bug Fixes

  • [sea-orm-migration] PostgreSQL drop_everything now drops custom types with CASCADE

Breaking Changes

Please 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>`
  • Removed runtime-actix feature flag. It's been an alias of runtime-tokio for more than a year, so there should be no impact.
  • Enabled sqlite-use-returning-for-3_35 by default. SQLite 3.35 was released in 2021, it should be the default by now.
  • Now implemented impl<T: ModelTrait + FromQueryResult> PartialModelTrait for T, there may be a potential conflict https://github.com/SeaQL/sea-orm/pull/2642
  • Now DeriveValueType will also TryFromU64 if applicable, there may be a potential conflict https://github.com/SeaQL/sea-orm/pull/2643
  • Now DeriveValueType also impl IntoActiveValue and NotU8, there may be a potential conflict
  • Added TryIntoModel 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/2599
fn 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,
error[E0119]: conflicting implementations of trait `sea_orm::FromQueryResult` for type `CakeWithFruit`
  |
> | #[derive(DerivePartialModel, FromQueryResult)]
  |          ------------------  ^^^^^^^^^^^^^^^ conflicting implementation for `CakeWithFruit`
trait IdenStatic {
    fn as_str(&self) -> &'static str; // added static lifetime
}
trait EntityName {
    fn table_name(&self) -> &'static str; // added static lifetime
}
// 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
error[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)
  • Removed DbBackend::get_query_builder() because QueryBuilder is not longer object safe.
  - fn get_query_builder(&self) -> Box<dyn QueryBuilder>
  • A number of methods has been removed from 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.
  • The 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 collision
  • with-bigdecimal is now removed from default features
  • RuntimeErr::SqlxError is now held in Arc to make DbErr clonable and smaller:
pub enum RuntimeErr {
    SqlxError(Arc<sqlx::error::Error>),

Upgrades

1.1.19 - 2025-11-11

Enhancements

Bug Fixes

1.1.17 - 2025-10-09

New Features

let mut opt = ConnectOptions::new(url);
opt.map_sqlx_postgres_opts(|pg_opt: PgConnectOptions| {
    pg_opt.ssl_mode(PgSslMode::Require)
});

1.1.16 - 2025-09-11

Bug Fixes

#[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""#,
);

Enhancements

1.1.15 - 2025-08-31

Enhancements

#[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>,
}
fn 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>;

Bug Fixes

1.1.14 - 2025-07-21

Enhancements

Bug Fixes

#[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

1.1.13 - 2025-06-29

New Features

// 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> ,
}

Enhancements

1.1.12 - 2025-05-27

Enhancements

Bug Fixes

#[derive(DeriveEntityModel)]
pub struct Model {
    #[sea_orm(column_name = "lAsTnAmE")]
    last_name: String,
}

assert!(matches!(Column::from_str("lAsTnAmE").unwrap(), Column::LastName));

1.1.11 - 2025-05-07

Enhancements

  • Added ActiveModelTrait::default_values
assert_eq!(
    fruit::ActiveModel::default_values(),
    fruit::ActiveModel {
        id: Set(0),
        name: Set("".into()),
        cake_id: Set(None),
        type_without_default: NotSet,
    },
);
// 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();

Bug fixes

assert_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
);

Upgrades

1.1.10 - 2025-04-14

Upgrades

1.1.9 - 2025-04-14

Enhancements

Bug fixes

House keeping

1.1.8 - 2025-03-30

New Features

  • Implement DeriveValueType for enum strings
#[derive(DeriveValueType)]
#[sea_orm(value_type = "String")]
pub enum Tag {
    Hard,
    Soft,
}

// `from_str` defaults to `std::str::FromStr::from_str`
impl std::str::FromStr for Tag {
    type Err = sea_orm::sea_query::ValueTypeErr;
    fn from_str(s: &str) -> Result<Self, Self::Err> { .. }
}

// `to_str` defaults to `std::string::ToString::to_string`.
impl std::fmt::Display for Tag {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { .. }
}

// you can override from_str and to_str with custom functions
#[derive(DeriveValueType)]
#[sea_orm(value_type = "String", from_str = "Tag::from_str", to_str = "Tag::to_str")]
pub enum Tag {
    Color,
    Grey,
}

impl Tag {
    fn from_str(s: &str) -> Result<Self, ValueTypeErr> { .. }

    fn to_str(&self) -> &'static str { .. }
}
// Model
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "host_network")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub ipaddress: IpNetwork,
    #[sea_orm(column_type = "Cidr")]
    pub network: IpNetwork,
}

// Schema
sea_query::Table::create()
    .table(host_network::Entity)
    .col(ColumnDef::new(host_network::Column::Id).integer().not_null().auto_increment().primary_key())
    .col(ColumnDef::new(host_network::Column::Ipaddress).inet().not_null())
    .col(ColumnDef::new(host_network::Column::Network).cidr().not_null())
    .to_owned();

// CRUD
host_network::ActiveModel {
    ipaddress: Set(IpNetwork::new(Ipv6Addr::new(..))),
    network: Set(IpNetwork::new(Ipv4Addr::new(..))),
    ..Default::default()
}

Enhancements

Bug fixes

House keeping

1.1.7 - 2025-03-02

New Features

#[derive(FromQueryResult)]
struct Cake {
    id: i32,
    name: String,
    #[sea_orm(nested)]
    bakery: Option<CakeBakery>,
}

#[derive(FromQueryResult)]
struct CakeBakery {
    #[sea_orm(from_alias = "bakery_id")]
    id: i32,
    #[sea_orm(from_alias = "bakery_name")]
    title: String,
}

let cake: Cake = cake::Entity::find()
    .select_only()
    .column(cake::Column::Id)
    .column(cake::Column::Name)
    .column_as(bakery::Column::Id, "bakery_id")
    .column_as(bakery::Column::Name, "bakery_name")
    .left_join(bakery::Entity)
    .order_by_asc(cake::Column::Id)
    .into_model()
    .one(&ctx.db)
    .await?
    .unwrap();

assert_eq!(
    cake,
    Cake {
        id: 1,
        name: "Cake".to_string(),
        bakery: Some(CakeBakery {
            id: 20,
            title: "Bakery".to_string(),
        })
    }
);
#[derive(DerivePartialModel)] // FromQueryResult is no longer needed
#[sea_orm(entity = "cake::Entity", from_query_result)]
struct Cake {
    id: i32,
    name: String,
    #[sea_orm(nested)]
    bakery: Option<Bakery>,
}

#[derive(DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity", from_query_result)]
struct Bakery {
    id: i32,
    #[sea_orm(from_col = "Name")]
    title: String,
}

// same as previous example, but without the custom selects
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,
    Cake {
        id: 1,
        name: "Cake".to_string(),
        bakery: Some(CakeBakery {
            id: 20,
            title: "Bakery".to_string(),
        })
    }
);
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct Cake {
    id: i32,
    name: String,
}

assert_eq!(
    Cake {
        id: 12,
        name: "Lemon Drizzle".to_owned(),
    }
    .into_active_model(),
    cake::ActiveModel {
        id: Set(12),
        name: Set("Lemon Drizzle".to_owned()),
        ..Default::default()
    }
);
// Order -> (many) 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?;

Enhancements

#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct Fruit {
    cake_id: Option<Option<i32>>,
}
pub async fn close(self) -> Result<(), DbErr> { .. } // existing
pub async fn close_by_ref(&self) -> Result<(), DbErr> { .. } // new

House Keeping

1.1.6 - 2025-02-24

New Features

// Model
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "image_model")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub id: i32,
    pub embedding: PgVector,
}

// Schema
sea_query::Table::create()
    .table(image_model::Entity.table_ref())
    .col(ColumnDef::new(Column::Id).integer().not_null().primary_key())
    .col(ColumnDef::new(Column::Embedding).vector(None).not_null())
    ..

// Insert
ActiveModel {
    id: NotSet,
    embedding: Set(PgVector::from(vec![1., 2., 3.])),
}
.insert(db)
.await?
  • Added Insert::exec_with_returning_keys & Insert::exec_with_returning_many (Postgres only)
assert_eq!(
    Entity::insert_many([
        ActiveModel { id: NotSet, name: Set("two".into()) },
        ActiveModel { id: NotSet, name: Set("three".into()) },
    ])
    .exec_with_returning_many(db)
    .await
    .unwrap(),
    [
        Model { id: 2, name: "two".into() },
        Model { id: 3, name: "three".into() },
    ]
);

assert_eq!(
    cakes_bakers::Entity::insert_many([
        cakes_bakers::ActiveModel {
            cake_id: Set(1),
            baker_id: Set(2),
        },
        cakes_bakers::ActiveModel {
            cake_id: Set(2),
            baker_id: Set(1),
        },
    ])
    .exec_with_returning_keys(db)
    .await
    .unwrap(),
    [(1, 2), (2, 1)]
);

Enhancements

Bug Fixes

House keeping

1.1.5 - 2025-02-14

New Features

  • Added Schema::json_schema_from_entity to construct a schema description in json for the given Entity

1.1.4 - 2025-01-10

Enhancements

1.1.3 - 2024-12-24

New Features

pub mod prelude;

pub mod sea_orm_active_enums;

pub mod baker;
pub mod bakery;
pub mod cake;
pub mod cakes_bakers;
pub mod customer;
pub mod lineitem;
pub mod order;

seaography::register_entity_modules!([
    baker,
    bakery,
    cake,
    cakes_bakers,
    customer,
    lineitem,
    order,
]);

seaography::register_active_enums!([
    sea_orm_active_enums::Tea,
    sea_orm_active_enums::Color,
]);

Enhancements

// this previously panics
let apple = cake_filling::ActiveModel {
    cake_id: ActiveValue::set(2),
    filling_id: ActiveValue::NotSet,
};
let orange = cake_filling::ActiveModel {
    cake_id: ActiveValue::NotSet,
    filling_id: ActiveValue::set(3),
};
assert_eq!(
    Insert::<cake_filling::ActiveModel>::new()
        .add_many([apple, orange])
        .build(DbBackend::Postgres)
        .to_string(),
    r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
);

Bug Fixes

1.1.2 - 2024-12-02

Enhancements

1.1.1 - 2024-11-04

Enhancements

use sea_orm::{tests_cfg::cake, Set};

struct Cake {
    id: i32,
    name: String,
}

impl From<Cake> for cake::ActiveModel {
    fn from(value: Cake) -> Self {
        Self {
            id: Set(value.id),
            name: Set(value.name),
        }
    }
}

1.1.0 - 2024-10-15

Versions

  • 1.1.0-rc.1: 2024-08-09
  • 1.1.0-rc.2: 2024-10-04
  • 1.1.0-rc.3: 2024-10-08

Enhancements

Upgrades

House keeping

1.0.1 - 2024-08-26

New Features

Breaking Changes

  • Changed ProxyDatabaseTrait methods to async. It's a breaking change, but it should have been part of the 1.0 release. The feature is behind the feature guard proxy, and we believe it shouldn't impact majority of users. https://github.com/SeaQL/sea-orm/pull/2278

Bug Fixes

1.0.0 - 2024-08-02

Versions

  • 1.0.0-rc.1: 2024-02-06
  • 1.0.0-rc.2: 2024-03-15
  • 1.0.0-rc.3: 2024-03-26
  • 1.0.0-rc.4: 2024-05-13
  • 1.0.0-rc.5: 2024-05-29
  • 1.0.0-rc.6: 2024-06-19
  • 1.0.0-rc.7: 2024-06-25

New Features

fn get_arity_of<E: EntityTrait>() -> usize {
    E::PrimaryKey::iter().count() // before; runtime
    <<E::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY // now; compile-time
}
#[derive(DeriveEntityModel)]
#[sea_orm(table_name = "user", rename_all = "camelCase")]
pub struct Model {
    #[sea_orm(primary_key)]
    id: i32,
    first_name: String, // firstName
    #[sea_orm(column_name = "lAsTnAmE")]
    last_name: String, // lAsTnAmE
}

#[derive(EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "camelCase")]
pub enum TestEnum {
    DefaultVariant, // defaultVariant
    #[sea_orm(rename = "kebab-case")]
    VariantKebabCase, // variant-kebab-case
    #[sea_orm(rename = "snake_case")]
    VariantSnakeCase, // variant_snake_case
    #[sea_orm(string_value = "CuStOmStRiNgVaLuE")]
    CustomStringValue, // CuStOmStRiNgVaLuE
}
// Remember to import `sea_orm_migration::schema::*`
use sea_orm_migration::{prelude::*, schema::*};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Users::Table)
                    .if_not_exists()
                    .col(pk_auto(Users::Id)) // Primary key with auto-increment
                    .col(uuid(Users::Pid)) // UUID column
                    .col(string_uniq(Users::Email)) // String column with unique constraint
                    .col(string(Users::Password)) // String column
                    .col(string(Users::ApiKey).unique_key())
                    .col(string(Users::Name))
                    .col(string_null(Users::ResetToken)) // Nullable string column
                    .col(timestamp_null(Users::ResetSentAt)) // Nullable timestamp column
                    .col(string_null(Users::EmailVerificationToken))
                    .col(timestamp_null(Users::EmailVerificationSentAt))
                    .col(timestamp_null(Users::EmailVerifiedAt))
                    .to_owned(),
            )
            .await
    }

    // ...
}

Enhancements

#[derive(DerivePartialModel)]
#[sea_orm(entity = "<entity::Model as ModelTrait>::Entity")]
struct EntityNameNotAIdent {
    #[sea_orm(from_col = "foo2")]
    _foo: i32,
    #[sea_orm(from_col = "bar2")]
    _bar: String,
}
let cf = Alias::new("cf");

assert_eq!(
    cake::Entity::find()
        .join_as(
            JoinType::LeftJoin,
            cake_filling::Relation::Cake.def().rev(),
            cf.clone()
        )
        .join(
            JoinType::LeftJoin,
            cake_filling::Relation::Filling.def().from_alias(cf)
        )
        .build(DbBackend::MySql)
        .to_string(),
    [
        "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
        "LEFT JOIN `cake_filling` AS `cf` ON `cake`.`id` = `cf`.`cake_id`",
        "LEFT JOIN `filling` ON `cf`.`filling_id` = `filling`.`id`",
    ]
    .join(" ")
);

Bug Fixes

Breaking changes

Upgrades

House keeping

1.0.0-rc.7 - 2024-06-25

Upgrades

1.0.0-rc.6 - 2024-06-19

Enhancements

Bug Fixes

1.0.0-rc.5 - 2024-05-29

New Features

fn get_arity_of<E: EntityTrait>() -> usize {
    E::PrimaryKey::iter().count() // before; runtime
    <<E::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY // now; compile-time
}
#[derive(DeriveEntityModel)]
#[sea_orm(table_name = "user", rename_all = "camelCase")]
pub struct Model {
    #[sea_orm(primary_key)]
    id: i32,
    first_name: String, // firstName
    #[sea_orm(column_name = "lAsTnAmE")]
    last_name: String, // lAsTnAmE
}

#[derive(EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "camelCase")]
pub enum TestEnum {
    DefaultVariant, // defaultVariant
    #[sea_orm(rename = "kebab-case")]
    VariantKebabCase, // variant-kebab-case
    #[sea_orm(rename = "snake_case")]
    VariantSnakeCase, // variant_snake_case
    #[sea_orm(string_value = "CuStOmStRiNgVaLuE")]
    CustomStringValue, // CuStOmStRiNgVaLuE
}

Enhancements

1.0.0-rc.4 - 2024-05-13

Enhancements

Upgrades

  • Upgrade sea-query to 0.31.0-rc.6
  • Upgrade sea-schema to 0.15.0-rc.6

House Keeping

1.0.0-rc.3 - 2024-03-26

Enhancements

1.0.0-rc.2 - 2024-03-15

Breaking Changes

Enhancements

#[derive(DerivePartialModel)]
#[sea_orm(entity = "<entity::Model as ModelTrait>::Entity")]
struct EntityNameNotAIdent {
    #[sea_orm(from_col = "foo2")]
    _foo: i32,
    #[sea_orm(from_col = "bar2")]
    _bar: String,
}
let cf = Alias::new("cf");

assert_eq!(
    cake::Entity::find()
        .join_as(
            JoinType::LeftJoin,
            cake_filling::Relation::Cake.def().rev(),
            cf.clone()
        )
        .join(
            JoinType::LeftJoin,
            cake_filling::Relation::Filling.def().from_alias(cf)
        )
        .build(DbBackend::MySql)
        .to_string(),
    [
        "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
        "LEFT JOIN `cake_filling` AS `cf` ON `cake`.`id` = `cf`.`cake_id`",
        "LEFT JOIN `filling` ON `cf`.`filling_id` = `filling`.`id`",
    ]
    .join(" ")
);

Upgrades

House keeping

1.0.0-rc.1 - 2024-02-06

New Features

// Remember to import `sea_orm_migration::schema::*`
use sea_orm_migration::{prelude::*, schema::*};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Users::Table)
                    .if_not_exists()
                    .col(pk_auto(Users::Id)) // Primary key with auto-increment
                    .col(uuid(Users::Pid)) // UUID column
                    .col(string_uniq(Users::Email)) // String column with unique constraint
                    .col(string(Users::Password)) // String column
                    .col(string(Users::ApiKey).unique_key())
                    .col(string(Users::Name))
                    .col(string_null(Users::ResetToken)) // Nullable string column
                    .col(timestamp_null(Users::ResetSentAt)) // Nullable timestamp column
                    .col(string_null(Users::EmailVerificationToken))
                    .col(timestamp_null(Users::EmailVerificationSentAt))
                    .col(timestamp_null(Users::EmailVerifiedAt))
                    .to_owned(),
            )
            .await
    }

    // ...
}

Breaking Changes

0.12.14 - 2024-02-05

0.12.12 - 2024-01-22

Bug Fixes

Enhancements

  • Added ConnectOptions::test_before_acquire

0.12.11 - 2024-01-14

New Features

Enhancements

Bug Fixes

House keeping

0.12.10 - 2023-12-14

New Features

Enhancements

Upgrades

0.12.9 - 2023-12-08

Enhancements

Upgrades

0.12.8 - 2023-12-04

Enhancements

Upgrades

0.12.7 - 2023-11-22

Enhancements

Upgrades

0.12.6 - 2023-11-13

New Features

0.12.5 - 2023-11-12

Bug Fixes

0.12.4 - 2023-10-19

New Features

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "json_struct_vec")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    #[sea_orm(column_type = "Json")]
    pub struct_vec: Vec<JsonColumn>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
pub struct JsonColumn {
    pub value: String,
}

Enhancements

Upgrades

0.12.3 - 2023-09-22

New Features

Enhancements

Bug Fixes

Upgrades

House keeping

0.12.2 - 2023-08-04

Enhancements

Bug fixes

0.12.1 - 2023-07-27

  • 0.12.0-rc.1: Yanked
  • 0.12.0-rc.2: 2023-05-19
  • 0.12.0-rc.3: 2023-06-22
  • 0.12.0-rc.4: 2023-07-08
  • 0.12.0-rc.5: 2023-07-22

New Features

#[async_trait::async_trait]
impl MigratorTrait for Migrator {
    // Override the name of migration table
    fn migration_table_name() -> sea_orm::DynIden {
        Alias::new("override_migration_table_name").into_iden()
    }
    ...
}
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    // By default, it's
    // `JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'`
    #[sea_orm(
        has_many = "super::fruit::Entity",
        on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
    )]
    TropicalFruit,
    // Or specify `condition_type = "any"` to override it,
    // `JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` OR `fruit`.`name` LIKE '%tropical%'`
    #[sea_orm(
        has_many = "super::fruit::Entity",
        on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
        condition_type = "any",
    )]
    OrTropicalFruit,
}
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "primary_key_of_12")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub id_1: String,
    ...
    #[sea_orm(primary_key, auto_increment = false)]
    pub id_12: bool,
}
#[derive(DerivePartialModel, FromQueryResult)]
#[sea_orm(entity = "Cake")]
struct PartialCake {
    name: String,
    #[sea_orm(
        from_expr = r#"SimpleExpr::FunctionCall(Func::upper(Expr::col((Cake, cake::Column::Name))))"#
    )]
    name_upper: String,
}

assert_eq!(
    cake::Entity::find()
        .into_partial_model::<PartialCake>()
        .into_statement(DbBackend::Sqlite)
        .to_string(),
    r#"SELECT "cake"."name", UPPER("cake"."name") AS "name_upper" FROM "cake""#
);
assert!(matches!(
    cake.into_active_model().insert(db).await
        .expect_err("Insert a row with duplicated primary key")
        .sql_err(),
    Some(SqlErr::UniqueConstraintViolation(_))
));

assert!(matches!(
    fk_cake.insert(db).await
        .expect_err("Insert a row with invalid foreign key")
        .sql_err(),
    Some(SqlErr::ForeignKeyConstraintViolation(_))
));
fn find_with_related<R>(self, r: R) -> SelectTwoMany<E, R>
    where R: EntityTrait, E: Related<R>;
fn find_with_linked<L, T>(self, l: L) -> SelectTwoMany<E, T>
    where L: Linked<FromEntity = E, ToEntity = T>, T: EntityTrait;

// boths yields `Vec<(E::Model, Vec<F::Model>)>`
  • Added DeriveValueType derive macro for custom wrapper types, implementations of the required traits will be provided, you can customize the column_type and array_type if needed https://github.com/SeaQL/sea-orm/pull/1720
#[derive(DeriveValueType)]
#[sea_orm(array_type = "Int")]
pub struct Integer(i32);

#[derive(DeriveValueType)]
#[sea_orm(column_type = "Boolean", array_type = "Bool")]
pub struct Boolbean(pub String);

#[derive(DeriveValueType)]
pub struct StringVec(pub Vec<String>);
#[derive(DeriveDisplay)]
enum DisplayTea {
    EverydayTea,
    #[sea_orm(display_value = "Breakfast Tea")]
    BreakfastTea,
}
assert_eq!(format!("{}", DisplayTea::EverydayTea), "EverydayTea");
assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast Tea");
let models: Vec<Model> = Entity::update_many()
    .col_expr(Column::Values, Expr::expr(..))
    .exec_with_returning(db)
    .await?;
#[derive(DeriveEntityModel)]
#[sea_orm(table_name = "hello")]
pub struct Model {
    #[sea_orm(default_expr = "Expr::current_timestamp()")]
    pub timestamp: DateTimeUtc,
}

assert_eq!(
    Column::Timestamp.def(),
    ColumnType::TimestampWithTimeZone.def()
        .default(Expr::current_timestamp())
);
enum DbErr {
    ConnectionAcquire(ConnAcquireErr),
    ..
}

enum ConnAcquireErr {
    Timeout,
    ConnectionClosed,
}

Seaography

Added Seaography integration https://github.com/SeaQL/sea-orm/pull/1599

  • Added DeriveEntityRelated macro which will implement seaography::RelationBuilder for RelatedEntity enumeration when the seaography feature is enabled

  • Added generation of seaography related information to sea-orm-codegen.

    The RelatedEntity enum is added in entities files by sea-orm-cli when flag seaography is set:

/// SeaORM Entity
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
pub enum RelatedEntity {
    #[sea_orm(entity = "super::bakery::Entity")]
    Bakery,
    #[sea_orm(entity = "super::cake_baker::Entity")]
    CakeBaker,
    #[sea_orm(entity = "super::cake::Entity")]
    Cake,
}

Enhancements

let migrations = Migrator::get_pending_migrations(db).await?;
assert_eq!(migrations.len(), 5);

let migration = migrations.get(0).unwrap();
assert_eq!(migration.name(), "m20220118_000002_create_fruit_table");
assert_eq!(migration.status(), MigrationStatus::Pending);
  • The postgres-array feature will be enabled when sqlx-postgres backend is selected https://github.com/SeaQL/sea-orm/pull/1565
  • Replace String parameters in API with Into<String> https://github.com/SeaQL/sea-orm/pull/1439
    • Implements IntoMockRow for any BTreeMap that is indexed by string impl IntoMockRow for BTreeMap<T, Value> where T: Into<String>
    • Converts any string value into ConnectOptions - impl From<T> for ConnectOptions where T: Into<String>
    • Changed the parameter of method ConnectOptions::new(T) where T: Into<String> to takes any string SQL
    • Changed the parameter of method Statement::from_string(DbBackend, T) where T: Into<String> to takes any string SQL
    • Changed the parameter of method Statement::from_sql_and_values(DbBackend, T, I) where I: IntoIterator<Item = Value>, T: Into<String> to takes any string SQL
    • Changed the parameter of method Transaction::from_sql_and_values(DbBackend, T, I) where I: IntoIterator<Item = Value>, T: Into<String> to takes any string SQL
    • Changed the parameter of method ConnectOptions::set_schema_search_path(T) where T: Into<String> to takes any string
    • Changed the parameter of method ColumnTrait::like(), ColumnTrait::not_like(), ColumnTrait::starts_with(), ColumnTrait::ends_with() and ColumnTrait::contains() to takes any string
  • Added sea_query::{DynIden, RcOrArc, SeaRc} to entity prelude https://github.com/SeaQL/sea-orm/pull/1661
  • Added expr, exprs and expr_as methods to QuerySelect trait https://github.com/SeaQL/sea-orm/pull/1702
  • Added DatabaseConnection::ping https://github.com/SeaQL/sea-orm/pull/1627
|db: DatabaseConnection| {
    assert!(db.ping().await.is_ok());
    db.clone().close().await;
    assert!(matches!(db.ping().await, Err(DbErr::ConnectionAcquire)));
}
// now, you can do:
let res = Bakery::insert_many(std::iter::empty())
    .on_empty_do_nothing()
    .exec(db)
    .await;

assert!(matches!(res, Ok(TryInsertResult::Empty)));
let on = OnConflict::column(Column::Id).do_nothing().to_owned();

// Existing behaviour
let res = Entity::insert_many([..]).on_conflict(on).exec(db).await;
assert!(matches!(res, Err(DbErr::RecordNotInserted)));

// New API; now you can:
let res =
Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await;
assert!(matches!(res, Ok(TryInsertResult::Conflicted)));

Bug Fixes

#[derive(EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(None)")]
pub enum StringValue {
    #[sea_orm(string_value = "")]
    Member1,
    #[sea_orm(string_value = "$$")]
    Member2,
}
// will now produce the following enum:
pub enum StringValueVariant {
    __Empty,
    _0x240x24,
}

Breaking changes

// then:

#[derive(Iden)]
#[iden = "category"]
pub struct CategoryEnum;

#[derive(Iden)]
pub enum Tea {
    Table,
    #[iden = "EverydayTea"]
    EverydayTea,
}

// now:

#[derive(DeriveIden)]
#[sea_orm(iden = "category")]
pub struct CategoryEnum;

#[derive(DeriveIden)]
pub enum Tea {
    Table,
    #[sea_orm(iden = "EverydayTea")]
    EverydayTea,
}

Upgrades

House keeping

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.11.1...0.12.1

0.11.3 - 2023-04-24

Enhancements

#[derive(FromQueryResult)]
struct GenericTest<T: TryGetable> {
    foo: i32,
    bar: T,
}
trait MyTrait {
    type Item: TryGetable;
}

#[derive(FromQueryResult)]
struct TraitAssociateTypeTest<T>
where
    T: MyTrait,
{
    foo: T::Item,
}

Bug Fixes

0.11.2 - 2023-03-25

Enhancements

0.11.1 - 2023-03-10

Bug Fixes

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "binary")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    #[sea_orm(column_type = "Binary(BlobSize::Blob(None))")]
    pub binary: Vec<u8>,
    #[sea_orm(column_type = "Binary(BlobSize::Blob(Some(10)))")]
    pub binary_10: Vec<u8>,
    #[sea_orm(column_type = "Binary(BlobSize::Tiny)")]
    pub binary_tiny: Vec<u8>,
    #[sea_orm(column_type = "Binary(BlobSize::Medium)")]
    pub binary_medium: Vec<u8>,
    #[sea_orm(column_type = "Binary(BlobSize::Long)")]
    pub binary_long: Vec<u8>,
    #[sea_orm(column_type = "VarBinary(10)")]
    pub var_binary: Vec<u8>,
}
  • The CLI command sea-orm-cli generate entity -u '<DB-URL>' --expanded-format will now generate the following code for each Binary or VarBinary columns in expanded format https://github.com/SeaQL/sea-orm/pull/1529
impl ColumnTrait for Column {
    type EntityName = Entity;
    fn def(&self) -> ColumnDef {
        match self {
            Self::Id => ColumnType::Integer.def(),
            Self::Binary => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(),
            Self::Binary10 => {
                ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(Some(10u32))).def()
            }
            Self::BinaryTiny => ColumnType::Binary(sea_orm::sea_query::BlobSize::Tiny).def(),
            Self::BinaryMedium => ColumnType::Binary(sea_orm::sea_query::BlobSize::Medium).def(),
            Self::BinaryLong => ColumnType::Binary(sea_orm::sea_query::BlobSize::Long).def(),
            Self::VarBinary => ColumnType::VarBinary(10u32).def(),
        }
    }
}

0.11.0 - 2023-02-07

  • 2023-02-02: 0.11.0-rc.1
  • 2023-02-04: 0.11.0-rc.2

New Features

SeaORM Core

SeaORM CLI

SeaORM Migration

Enhancements

Upgrades

House Keeping

Bug Fixes

Breaking Changes

// then
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>;
// now; ColIdx can be `&str` or `usize`
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError>;

So if you implemented it yourself:

impl TryGetable for XXX {
-   fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
+   fn try_get_by<I: sea_orm::ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
-       let value: YYY = res.try_get(pre, col).map_err(TryGetError::DbErr)?;
+       let value: YYY = res.try_get_by(idx).map_err(TryGetError::DbErr)?;
        ..
    }
}
#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
    async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        // ...
    }

    // ...
}
let res = Update::one(cake::ActiveModel {
        name: Set("Cheese Cake".to_owned()),
        ..model.into_active_model()
    })
    .exec(&db)
    .await;

// then
assert_eq!(
    res,
    Err(DbErr::RecordNotFound(
        "None of the database rows are affected".to_owned()
    ))
);

// now
assert_eq!(res, Err(DbErr::RecordNotUpdated));
  • sea_orm::ColumnType was replaced by sea_query::ColumnType https://github.com/SeaQL/sea-orm/pull/1395
    • Method ColumnType::def was moved to ColumnTypeTrait
    • ColumnType::Binary becomes a tuple variant which takes in additional option sea_query::BlobSize
    • ColumnType::Custom takes a sea_query::DynIden instead of String and thus a new method custom is added (note the lowercase)
// Compact Entity
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
-   #[sea_orm(column_type = r#"Custom("citext".to_owned())"#)]
+   #[sea_orm(column_type = r#"custom("citext")"#)]
    pub column: String,
}
// Expanded Entity
impl ColumnTrait for Column {
    type EntityName = Entity;

    fn def(&self) -> ColumnDef {
        match self {
-           Self::Column => ColumnType::Custom("citext".to_owned()).def(),
+           Self::Column => ColumnType::custom("citext").def(),
        }
    }
}

Miscellaneous

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.10.0...0.11.0

0.10.7 - 2023-01-19

Bug Fixes

0.10.6 - 2022-12-23

Enhancements

Bug Fixes

0.10.5 - 2022-12-02

New Features

Bug Fixes

Enhancements

0.10.4 - 2022-11-24

Bug Fixes

Enhancements

0.10.3 - 2022-11-14

Bug Fixes

Enhancements

House Keeping

0.10.2 - 2022-11-06

Enhancements

Bug Fixes

Upgrades

  • Update MSRV to 1.65

0.10.1 - 2022-10-27

Enhancements

Bug Fixes

House Keeping

Upgrades

0.10.0 - 2022-10-23

New Features

Enhancements

Bug Fixes

Breaking Changes

enum ColumnType {
    // then
    Enum(String, Vec<String>)

    // now
    Enum {
        /// Name of enum
        name: DynIden,
        /// Variants of enum
        variants: Vec<DynIden>,
    }
    ...
}

// example

#[derive(Iden)]
enum TeaEnum {
    #[iden = "tea"]
    Enum,
    #[iden = "EverydayTea"]
    EverydayTea,
    #[iden = "BreakfastTea"]
    BreakfastTea,
}

// then
ColumnDef::new(active_enum_child::Column::Tea)
    .enumeration("tea", vec!["EverydayTea", "BreakfastTea"])

// now
ColumnDef::new(active_enum_child::Column::Tea)
    .enumeration(TeaEnum::Enum, [TeaEnum::EverydayTea, TeaEnum::BreakfastTea])
  • A new method array_type was added to ValueType:
impl sea_orm::sea_query::ValueType for MyType {
    fn array_type() -> sea_orm::sea_query::ArrayType {
        sea_orm::sea_query::ArrayType::TypeName
    }
    ...
}
  • ActiveEnum::name() changed return type to DynIden:
#[derive(Debug, Iden)]
#[iden = "category"]
pub struct CategoryEnum;

impl ActiveEnum for Category {
    // then
    fn name() -> String {
        "category".to_owned()
    }

    // now
    fn name() -> DynIden {
        SeaRc::new(CategoryEnum)
    }
    ...
}

House Keeping

Integration

Upgrades

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.9.0...0.10.0

0.9.3 - 2022-09-30

Enhancements

Bug Fixes

0.9.2 - 2022-08-20

Enhancements

House Keeping

Notes

In this minor release, we removed time v0.1 from the dependency graph

0.9.1 - 2022-07-22

Enhancements

Bug Fixes

House Keeping

0.9.0 - 2022-07-17

New Features

Enhancements

Upgrades

House Keeping

Bug Fixes

Breaking Changes

  • SelectTwoMany::one() has been dropped https://github.com/SeaQL/sea-orm/pull/813, you can get (Entity, Vec<RelatedEntity>) by first querying a single model from Entity, then use [ModelTrait::find_related] on the model.
  • Feature flag revamp

    We now adopt the weak dependency syntax in Cargo. That means the flags ["sqlx-json", "sqlx-chrono", "sqlx-decimal", "sqlx-uuid", "sqlx-time"] are not needed and now removed. Instead, with-time will enable sqlx?/time only if sqlx is already enabled. As a consequence, now the features with-json, with-chrono, with-rust_decimal, with-uuid, with-time will not be enabled as a side-effect of enabling sqlx.

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.8.0...0.9.0

sea-orm-migration 0.8.3

0.8.0 - 2022-05-10

New Features

Enhancements

Bug Fixes

Breaking Changes

  • Migration utilities are moved from sea-schema to sea-orm repo, under a new sub-crate sea-orm-migration. sea_schema::migration::prelude should be replaced by sea_orm_migration::prelude in all migration files

Upgrades

Fixed Issues

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.7.1...0.8.0

0.7.1 - 2022-03-26

  • Fix sea-orm-cli error
  • Fix sea-orm cannot build without with-json

0.7.0 - 2022-03-26

New Features

Enhancements

Bug Fixes

Breaking Changes

  • Exclude mock from default features by @billy1624 https://github.com/SeaQL/sea-orm/pull/562
  • create_table_from_entity will no longer create index for MySQL, please use the new method create_index_from_entity

Documentations

Fixed Issues

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.6.0...0.7.0

0.6.0 - 2022-02-07

New Features

Enhancements

Bug Fixes

Breaking Changes

Fixed Issues

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.5.0...0.6.0

0.5.0 - 2022-01-01

Fixed Issues

Merged PRs

Breaking Changes

  • ActiveModel::insert and ActiveModel::update return Model instead of ActiveModel
  • Method ActiveModelBehavior::after_save takes Model as input instead of ActiveModel
  • Rename method sea_orm::unchanged_active_value_not_intended_for_public_use to sea_orm::Unchanged
  • Rename method ActiveValue::unset to ActiveValue::not_set
  • Rename method ActiveValue::is_unset to ActiveValue::is_not_set
  • PartialEq of ActiveValue will also check the equality of state instead of just checking the equality of value

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.4.2...0.5.0

0.4.2 - 2021-12-12

Fixed Issues

Merged PRs

0.4.1 - 2021-12-05

Fixed Issues

Merged PRs

0.4.0 - 2021-11-19

Fixed Issues

Merged PRs

Breaking Changes

  • Refactor paginate() & count() utilities into PaginatorTrait. You can use the paginator as usual but you might need to import PaginatorTrait manually when upgrading from the previous version.
    use futures::TryStreamExt;
    use sea_orm::{entity::*, query::*, tests_cfg::cake};
    
    let mut cake_stream = cake::Entity::find()
        .order_by_asc(cake::Column::Id)
        .paginate(db, 50)
        .into_stream();
    
    while let Some(cakes) = cake_stream.try_next().await? {
        // Do something on cakes: Vec<cake::Model>
    }
    
  • The helper struct Schema converting EntityTrait into different sea-query statements now has to be initialized with DbBackend.
    use sea_orm::{tests_cfg::*, DbBackend, Schema};
    use sea_orm::sea_query::TableCreateStatement;
    
    // 0.3.x
    let _: TableCreateStatement = Schema::create_table_from_entity(cake::Entity);
    
    // 0.4.x
    let schema: Schema = Schema::new(DbBackend::MySql);
    let _: TableCreateStatement = schema.create_table_from_entity(cake::Entity);
    
  • When performing insert or update operation on ActiveModel against PostgreSQL, RETURNING clause will be used to perform select in a single SQL statement.
    // For PostgreSQL
    cake::ActiveModel {
        name: Set("Apple Pie".to_owned()),
        ..Default::default()
    }
    .insert(&postgres_db)
    .await?;
    
    assert_eq!(
        postgres_db.into_transaction_log(),
        vec![Transaction::from_sql_and_values(
            DbBackend::Postgres,
            r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#,
            vec!["Apple Pie".into()]
        )]);
    
    // For MySQL & SQLite
    cake::ActiveModel {
        name: Set("Apple Pie".to_owned()),
        ..Default::default()
    }
    .insert(&other_db)
    .await?;
    
    assert_eq!(
        other_db.into_transaction_log(),
        vec![
            Transaction::from_sql_and_values(
                DbBackend::MySql,
                r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
                vec!["Apple Pie".into()]
            ),
            Transaction::from_sql_and_values(
                DbBackend::MySql,
                r#"SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = ? LIMIT ?"#,
                vec![15.into(), 1u64.into()]
            )]);
    

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.3.2...0.4.0

0.3.2 - 2021-11-03

Fixed Issues

Merged PRs

0.3.1 - 2021-10-23

(We are changing our Changelog format from now on)

Fixed Issues

(The following is generated by GitHub)

Merged PRs

0.3.0 - 2021-10-15

https://www.sea-ql.org/SeaORM/blog/2021-10-15-whats-new-in-0.3.0

  • Built-in Rocket support
  • ConnectOptions
let mut opt = ConnectOptions::new("protocol://username:password@host/database".to_owned());
opt.max_connections(100)
    .min_connections(5)
    .connect_timeout(Duration::from_secs(8))
    .idle_timeout(Duration::from_secs(8));
let db = Database::connect(opt).await?;
  • [#211] Throw error if none of the db rows are affected
assert_eq!(
    Update::one(cake::ActiveModel {
        name: Set("Cheese Cake".to_owned()),
        ..model.into_active_model()
    })
    .exec(&db)
    .await,
    Err(DbErr::RecordNotFound(
        "None of the database rows are affected".to_owned()
    ))
);

// update many remains the same
assert_eq!(
    Update::many(cake::Entity)
        .col_expr(cake::Column::Name, Expr::value("Cheese Cake".to_owned()))
        .filter(cake::Column::Id.eq(2))
        .exec(&db)
        .await,
    Ok(UpdateResult { rows_affected: 0 })
);
  • [#223] ActiveValue::take() & ActiveValue::into_value() without unwrap()
  • [#205] Drop Default trait bound of PrimaryKeyTrait::ValueType
  • [#222] Transaction & streaming
  • [#210] Update ActiveModelBehavior API
  • [#240] Add derive DeriveIntoActiveModel and IntoActiveValue trait
  • [#237] Introduce optional serde support for model code generation
  • [#246] Add #[automatically_derived] to all derived implementations

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.2.6...0.3.0

0.2.6 - 2021-10-09

  • [#224] [sea-orm-cli] Date & Time column type mapping
  • Escape rust keywords with r# raw identifier

0.2.5 - 2021-10-06

  • [#227] Resolve "Inserting actual none value of Option results in panic"
  • [#219] [sea-orm-cli] Add --tables option
  • [#189] Add debug_query and debug_query_stmt macro

0.2.4 - 2021-10-01

https://www.sea-ql.org/SeaORM/blog/2021-10-01-whats-new-in-0.2.4

  • [#186] [sea-orm-cli] Foreign key handling
  • [#191] [sea-orm-cli] Unique key handling
  • [#182] find_linked join with alias
  • [#202] Accept both postgres:// and postgresql://
  • [#208] Support fetching T, (T, U), (T, U, P) etc
  • [#209] Rename column name & column enum variant
  • [#207] Support chrono::NaiveDate & chrono::NaiveTime
  • Support Condition::not (from sea-query)

0.2.3 - 2021-09-22

  • [#152] DatabaseConnection impl Clone
  • [#175] Impl TryGetableMany for different types of generics
  • Codegen TimestampWithTimeZone fixup

0.2.2 - 2021-09-18

  • [#105] Compact entity format
  • [#132] Add ActiveModel insert & update
  • [#129] Add set method to UpdateMany
  • [#118] Initial lock support
  • [#167] Add FromQueryResult::find_by_statement

0.2.1 - 2021-09-04

  • Update dependencies

0.2.0 - 2021-09-03

  • [#37] Rocket example
  • [#114] log crate and env-logger
  • [#103] InsertResult to return the primary key's type
  • [#89] Represent several relations between same types by Linked
  • [#59] Transforming an Entity into TableCreateStatement

Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.1.3...0.2.0

0.1.3 - 2021-08-30

  • [#108] Remove impl TryGetable for Option

0.1.2 - 2021-08-23

  • [#68] Added DateTimeWithTimeZone as supported attribute type
  • [#70] Generate arbitrary named entity
  • [#80] Custom column name
  • [#81] Support join on multiple columns
  • [#99] Implement FromStr for ColumnTrait

0.1.1 - 2021-08-08

  • Early release of SeaORM