From ef1eb577b726d2d10fa6ac6b31f1f86790d4c34d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 28 Sep 2021 16:48:51 +0800 Subject: [PATCH 1/6] Update `ActiveModelBehavior` API --- src/entity/active_model.rs | 50 +++++++++++++++++------------ src/entity/prelude.rs | 9 +++--- src/executor/insert.rs | 4 +-- tests/common/bakery_chain/applog.rs | 1 + tests/common/setup/schema.rs | 1 + tests/timestamp_tests.rs | 1 + 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index cfcb0bbd..7cdd9071 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -69,11 +69,11 @@ pub trait ActiveModelTrait: Clone + Debug { async fn insert(self, db: &DatabaseConnection) -> Result where + Self: ActiveModelBehavior, ::Model: IntoActiveModel, { - let am = self; - let exec = ::insert(am).exec(db); - let res = exec.await?; + let am = ActiveModelBehavior::before_save(self, true, db)?; + let res = ::insert(am).exec(db).await?; // Assume valid last_insert_id is not equals to Default::default() if res.last_insert_id != <::PrimaryKey as PrimaryKeyTrait>::ValueType::default() @@ -81,18 +81,23 @@ pub trait ActiveModelTrait: Clone + Debug { let found = ::find_by_id(res.last_insert_id) .one(db) .await?; - match found { + let am = match found { Some(model) => Ok(model.into_active_model()), None => Err(DbErr::Exec("Failed to find inserted item".to_owned())), - } + }?; + ActiveModelBehavior::after_save(am, true, db) } else { Ok(Self::default()) } } - async fn update(self, db: &DatabaseConnection) -> Result { - let exec = Self::Entity::update(self).exec(db); - exec.await + async fn update(self, db: &DatabaseConnection) -> Result + where + Self: ActiveModelBehavior, + { + let am = ActiveModelBehavior::before_save(self, false, db)?; + let am = Self::Entity::update(am).exec(db).await?; + ActiveModelBehavior::after_save(am, false, db) } /// Insert the model if primary key is unset, update otherwise. @@ -103,7 +108,6 @@ pub trait ActiveModelTrait: Clone + Debug { ::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(); @@ -117,7 +121,6 @@ pub trait ActiveModelTrait: Clone + Debug { } else { am = am.update(db).await?; } - am = ActiveModelBehavior::after_save(am); Ok(am) } @@ -126,14 +129,16 @@ pub trait ActiveModelTrait: Clone + Debug { where Self: ActiveModelBehavior, { - let mut am = self; - am = ActiveModelBehavior::before_delete(am); - let exec = Self::Entity::delete(am).exec(db); - exec.await + let am = ActiveModelBehavior::before_delete(self, db)?; + let am_clone = am.clone(); + let delete_res = Self::Entity::delete(am).exec(db).await?; + ActiveModelBehavior::after_delete(am_clone, db)?; + Ok(delete_res) } } /// Behaviors for users to override +#[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { /// Create a new ActiveModel with default values. Also used by `Default::default()`. fn new() -> Self { @@ -141,18 +146,23 @@ pub trait ActiveModelBehavior: ActiveModelTrait { } /// Will be called before saving - fn before_save(self) -> Self { - self + fn before_save(self, insert: bool, db: &DatabaseConnection) -> Result { + Ok(self) } /// Will be called after saving - fn after_save(self) -> Self { - self + fn after_save(self, insert: bool, db: &DatabaseConnection) -> Result { + Ok(self) } /// Will be called before deleting - fn before_delete(self) -> Self { - self + fn before_delete(self, db: &DatabaseConnection) -> Result { + Ok(self) + } + + /// Will be called after deleting + fn after_delete(self, db: &DatabaseConnection) -> Result { + Ok(self) } } diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 8d87a4b2..72143c9c 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,9 +1,10 @@ pub use crate::{ error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, - DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity, - DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, EntityName, EntityTrait, - EnumIter, ForeignKeyAction, Iden, IdenStatic, Linked, ModelTrait, PrimaryKeyToColumn, - PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value, + DatabaseConnection, DbConn, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, + DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveModel, DerivePrimaryKey, + DeriveRelation, EntityName, EntityTrait, EnumIter, ForeignKeyAction, Iden, IdenStatic, Linked, + ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, + RelationDef, RelationTrait, Select, Value, }; #[cfg(feature = "with-json")] diff --git a/src/executor/insert.rs b/src/executor/insert.rs index d580f110..a44867f7 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,6 +1,6 @@ use crate::{ - error::*, ActiveModelTrait, DatabaseConnection, DbBackend, EntityTrait, Insert, PrimaryKeyTrait, - Statement, TryFromU64, + error::*, ActiveModelTrait, DatabaseConnection, DbBackend, EntityTrait, Insert, + PrimaryKeyTrait, Statement, TryFromU64, }; use sea_query::InsertStatement; use std::{future::Future, marker::PhantomData}; diff --git a/tests/common/bakery_chain/applog.rs b/tests/common/bakery_chain/applog.rs index 03b06d61..1f83ca8f 100644 --- a/tests/common/bakery_chain/applog.rs +++ b/tests/common/bakery_chain/applog.rs @@ -5,6 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, + pub action: String, pub json: Json, pub created_at: DateTimeWithTimeZone, } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 64f31dfe..550a371b 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -301,6 +301,7 @@ pub async fn create_log_table(db: &DbConn) -> Result { .auto_increment() .primary_key(), ) + .col(ColumnDef::new(applog::Column::Action).string().not_null()) .col(ColumnDef::new(applog::Column::Json).json().not_null()) .col( ColumnDef::new(applog::Column::CreatedAt) diff --git a/tests/timestamp_tests.rs b/tests/timestamp_tests.rs index 1897323c..beca5593 100644 --- a/tests/timestamp_tests.rs +++ b/tests/timestamp_tests.rs @@ -16,6 +16,7 @@ async fn main() -> Result<(), DbErr> { pub async fn create_applog(db: &DatabaseConnection) -> Result<(), DbErr> { let log = applog::Model { id: 1, + action: "Testing".to_owned(), json: Json::String("HI".to_owned()), created_at: "2021-09-17T17:50:20+08:00".parse().unwrap(), }; From 08595c97290f9b9b8397fc68b641e27b65114952 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 4 Oct 2021 11:45:24 +0800 Subject: [PATCH 2/6] Try `DbErr::Custom` --- src/error.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 09f80b0a..d8129583 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,11 @@ -#[derive(Debug, PartialEq)] +use std::error::Error; + +#[derive(Debug)] pub enum DbErr { Conn(String), Exec(String), Query(String), + Custom(Box), } impl std::error::Error for DbErr {} @@ -13,6 +16,7 @@ impl std::fmt::Display for DbErr { Self::Conn(s) => write!(f, "Connection Error: {}", s), Self::Exec(s) => write!(f, "Execution Error: {}", s), Self::Query(s) => write!(f, "Query Error: {}", s), + Self::Custom(e) => write!(f, "Custom Error: {}", e), } } } From b6c12963338773470bdc4d00becaf21cfad67baa Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 12 Oct 2021 13:07:02 +0800 Subject: [PATCH 3/6] Fix conflict --- src/error.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index d8129583..fc68ffe5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,10 @@ -use std::error::Error; - #[derive(Debug)] pub enum DbErr { Conn(String), Exec(String), Query(String), - Custom(Box), + RecordNotFound(String), + Custom(String), } impl std::error::Error for DbErr {} @@ -16,7 +15,8 @@ impl std::fmt::Display for DbErr { Self::Conn(s) => write!(f, "Connection Error: {}", s), Self::Exec(s) => write!(f, "Execution Error: {}", s), Self::Query(s) => write!(f, "Query Error: {}", s), - Self::Custom(e) => write!(f, "Custom Error: {}", e), + Self::RecordNotFound(s) => write!(f, "RecordNotFound Error: {}", s), + Self::Custom(s) => write!(f, "Custom Error: {}", s), } } } From 8900d034921d4470f729d155a51a6e87fcea6ef6 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 12 Oct 2021 20:42:15 +0800 Subject: [PATCH 4/6] Merge fix --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index fc68ffe5..f39aee72 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum DbErr { Conn(String), Exec(String), From 75e625fee9615e5567bc47902671c041840fe4d6 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 12 Oct 2021 20:42:42 +0800 Subject: [PATCH 5/6] ActiveModelBehavior without db connection --- src/entity/active_model.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index d6625af7..e0173a6d 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -106,12 +106,11 @@ pub trait ActiveModelTrait: Clone + Debug { async fn insert<'a, C>(self, db: &'a C) -> Result where - Self: ActiveModelBehavior, ::Model: IntoActiveModel, + Self: ActiveModelBehavior + 'a, C: ConnectionTrait<'a>, - Self: 'a, { - let am = ActiveModelBehavior::before_save(self, true, db)?; + let am = ActiveModelBehavior::before_save(self, true)?; let res = ::insert(am).exec(db).await?; let found = ::find_by_id(res.last_insert_id) .one(db) @@ -124,20 +123,20 @@ pub trait ActiveModelTrait: Clone + Debug { async fn update<'a, C>(self, db: &'a C) -> Result where + Self: ActiveModelBehavior + 'a, C: ConnectionTrait<'a>, - Self: 'a, { - let am = ActiveModelBehavior::before_save(self, false, db)?; + let am = ActiveModelBehavior::before_save(self, false)?; let am = Self::Entity::update(am).exec(db).await?; - ActiveModelBehavior::after_save(am, false, db) + ActiveModelBehavior::after_save(am, false) } /// Insert the model if primary key is unset, update otherwise. /// Only works if the entity has auto increment primary key. async fn save<'a, C>(self, db: &'a C) -> Result where - Self: ActiveModelBehavior + 'a, ::Model: IntoActiveModel, + Self: ActiveModelBehavior + 'a, C: ConnectionTrait<'a>, { let mut am = self; @@ -163,10 +162,10 @@ pub trait ActiveModelTrait: Clone + Debug { Self: ActiveModelBehavior + 'a, C: ConnectionTrait<'a>, { - let am = ActiveModelBehavior::before_delete(self, db)?; + let am = ActiveModelBehavior::before_delete(self)?; let am_clone = am.clone(); let delete_res = Self::Entity::delete(am).exec(db).await?; - ActiveModelBehavior::after_delete(am_clone, db)?; + ActiveModelBehavior::after_delete(am_clone)?; Ok(delete_res) } } @@ -180,22 +179,22 @@ pub trait ActiveModelBehavior: ActiveModelTrait { } /// Will be called before saving - fn before_save(self, insert: bool, db: &DatabaseConnection) -> Result { + fn before_save(self, insert: bool) -> Result { Ok(self) } /// Will be called after saving - fn after_save(self, insert: bool, db: &DatabaseConnection) -> Result { + fn after_save(self, insert: bool) -> Result { Ok(self) } /// Will be called before deleting - fn before_delete(self, db: &DatabaseConnection) -> Result { + fn before_delete(self) -> Result { Ok(self) } /// Will be called after deleting - fn after_delete(self, db: &DatabaseConnection) -> Result { + fn after_delete(self) -> Result { Ok(self) } } From 6ecc3138c307072b896f65e5ab28129a444bc325 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 12 Oct 2021 21:14:40 +0800 Subject: [PATCH 6/6] Use new `ActiveValue::into_value()` --- src/entity/active_model.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index e0173a6d..4692bce6 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -10,8 +10,7 @@ pub struct ActiveValue where V: Into, { - // Don't want to call ActiveValue::unwrap() and cause panic - pub(self) value: Option, + value: Option, state: ActiveValueState, } @@ -74,7 +73,7 @@ pub trait ActiveModelTrait: Clone + Debug { macro_rules! next { () => { if let Some(col) = cols.next() { - if let Some(val) = self.get(col.into_column()).value { + if let Some(val) = self.get(col.into_column()).into_value() { val } else { return None;