diff --git a/Cargo.toml b/Cargo.toml index d114854639..65adce04e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ path = "src/lib.rs" [dependencies] async-stream = { version = "^0.3" } +async-trait = { version = "^0.1" } chrono = { version = "^0", optional = true } futures = { version = "^0.3" } futures-util = { version = "^0.3" } diff --git a/sea-orm-macros/src/derives/active_model.rs b/sea-orm-macros/src/derives/active_model.rs index 3eb821eb6a..3ca843ee18 100644 --- a/sea-orm-macros/src/derives/active_model.rs +++ b/sea-orm-macros/src/derives/active_model.rs @@ -36,24 +36,6 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result),* } - impl ActiveModel { - pub async fn insert(self, db: &sea_orm::DatabaseConnection) -> Result { - sea_orm::insert_and_select_active_model::(self, db).await - } - - pub async fn update(self, db: &sea_orm::DatabaseConnection) -> Result { - sea_orm::update_active_model::(self, db).await - } - - pub async fn save(self, db: &sea_orm::DatabaseConnection) -> Result { - sea_orm::save_active_model::(self, db).await - } - - pub async fn delete(self, db: &sea_orm::DatabaseConnection) -> Result { - sea_orm::delete_active_model::(self, db).await - } - } - impl Default for ActiveModel { fn default() -> Self { ::new() diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 7eee81f331..f6edf47c42 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -2,6 +2,7 @@ use crate::{ error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, Value, }; +use async_trait::async_trait; use std::fmt::Debug; #[derive(Clone, Debug, Default)] @@ -50,6 +51,7 @@ where ActiveValue::unchanged(value) } +#[async_trait] pub trait ActiveModelTrait: Clone + Debug { type Entity: EntityTrait; @@ -65,9 +67,71 @@ pub trait ActiveModelTrait: Clone + Debug { fn default() -> Self; - // below is not yet possible. right now we define these methods in DeriveActiveModel - // fn save(self, db: &DatabaseConnection) -> impl Future>; - // fn delete(self, db: &DatabaseConnection) -> impl Future>; + async fn insert(self, db: &DatabaseConnection) -> Result + where + ::Model: IntoActiveModel, + { + let am = self; + let exec = ::insert(am).exec(db); + let res = exec.await?; + // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value + // FIXME: Assumed valid last_insert_id is not equals to Default::default() + if <::PrimaryKey as PrimaryKeyTrait>::auto_increment() + && res.last_insert_id != <::PrimaryKey as PrimaryKeyTrait>::ValueType::default() + { + let find = ::find_by_id(res.last_insert_id).one(db); + let found = find.await; + let model: Option<::Model> = found?; + match model { + Some(model) => Ok(model.into_active_model()), + None => Err(DbErr::Exec("Failed to find inserted item: {}".to_owned())), + } + } else { + Ok(Self::default()) + } + } + + async fn update(self, db: &DatabaseConnection) -> Result { + let exec = Self::Entity::update(self).prepare_filters().exec(db); + exec.await + } + + /// Insert the model if primary key is unset, update otherwise. + /// Only works if the entity has auto increment primary key. + async fn save(self, db: &DatabaseConnection) -> Result + where + Self: ActiveModelBehavior, + ::Model: IntoActiveModel, + { + let mut am = self; + am = ActiveModelBehavior::before_save(am); + let mut is_update = true; + for key in ::PrimaryKey::iter() { + let col = key.into_column(); + if am.is_unset(col) { + is_update = false; + break; + } + } + if !is_update { + am = am.insert(db).await?; + } else { + am = am.update(db).await?; + } + am = ActiveModelBehavior::after_save(am); + Ok(am) + } + + /// Delete an active model by its primary key + async fn delete(self, db: &DatabaseConnection) -> Result + where + Self: ActiveModelBehavior, + { + let mut am = self; + am = ActiveModelBehavior::before_delete(am); + let exec = Self::Entity::delete(am).exec(db); + exec.await + } } /// Behaviors for users to override @@ -185,83 +249,3 @@ where self.value.as_ref() == other.value.as_ref() } } - -/// Insert the model if primary key is unset, update otherwise. -/// Only works if the entity has auto increment primary key. -pub async fn save_active_model(mut am: A, db: &DatabaseConnection) -> Result -where - A: ActiveModelBehavior + ActiveModelTrait, - E::Model: IntoActiveModel, - E: EntityTrait, -{ - am = ActiveModelBehavior::before_save(am); - let mut is_update = true; - for key in E::PrimaryKey::iter() { - let col = key.into_column(); - if am.is_unset(col) { - is_update = false; - break; - } - } - if !is_update { - am = insert_and_select_active_model::(am, db).await?; - } else { - am = update_active_model::(am, db).await?; - } - am = ActiveModelBehavior::after_save(am); - Ok(am) -} - -pub async fn insert_and_select_active_model( - am: A, - db: &DatabaseConnection, -) -> Result -where - A: ActiveModelTrait, - E::Model: IntoActiveModel, - E: EntityTrait, -{ - let exec = E::insert(am).exec(db); - let res = exec.await?; - // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value - // FIXME: Assumed valid last_insert_id is not equals to Default::default() - if ::auto_increment() - && res.last_insert_id != ::ValueType::default() - { - let find = E::find_by_id(res.last_insert_id).one(db); - let found = find.await; - let model: Option = found?; - match model { - Some(model) => Ok(model.into_active_model()), - None => Err(DbErr::Exec(format!( - "Failed to find inserted item: {}", - E::default().to_string(), - ))), - } - } else { - Ok(A::default()) - } -} - -pub async fn update_active_model(am: A, db: &DatabaseConnection) -> Result -where - A: ActiveModelTrait, - E: EntityTrait, -{ - let exec = E::update(am).prepare_filters().exec(db); - exec.await -} - -/// Delete an active model by its primary key -pub async fn delete_active_model( - mut am: A, - db: &DatabaseConnection, -) -> Result -where - A: ActiveModelBehavior + ActiveModelTrait, - E: EntityTrait, -{ - am = ActiveModelBehavior::before_delete(am); - let exec = E::delete(am).exec(db); - exec.await -} diff --git a/src/entity/model.rs b/src/entity/model.rs index 4774e1dc85..a2d1868f08 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -2,7 +2,7 @@ use crate::{DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Selec pub use sea_query::Value; use std::fmt::Debug; -pub trait ModelTrait: Clone + Debug { +pub trait ModelTrait: Clone + Send + Debug { type Entity: EntityTrait; fn get(&self, c: ::Column) -> Value; diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index b9c381d400..463f1482e9 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -6,6 +6,7 @@ use std::fmt::Debug; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { type ValueType: Sized + + Send + Default + Debug + PartialEq