Cont. feat: expose database connection to ActiveModelBehaviour
's methods (#1328)
* feat: expose database connection to `ActiveModelBehaviour`'s methods (#1145) * Make ActiveModelTrait async * Add tests * refactoring Co-authored-by: teenjuna <53595243+teenjuna@users.noreply.github.com>
This commit is contained in:
parent
a9ec15ea02
commit
e2b796b093
@ -291,11 +291,11 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_save(self, true)?;
|
||||
let am = ActiveModelBehavior::before_save(self, db, true).await?;
|
||||
let model = <Self::Entity as EntityTrait>::insert(am)
|
||||
.exec_with_returning(db)
|
||||
.await?;
|
||||
Self::after_save(model, true)
|
||||
Self::after_save(model, db, true).await
|
||||
}
|
||||
|
||||
/// Perform the `UPDATE` operation on an ActiveModel
|
||||
@ -413,9 +413,9 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_save(self, false)?;
|
||||
let am = ActiveModelBehavior::before_save(self, db, false).await?;
|
||||
let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
|
||||
Self::after_save(model, false)
|
||||
Self::after_save(model, db, false).await
|
||||
}
|
||||
|
||||
/// Insert the model if primary key is `NotSet`, update otherwise.
|
||||
@ -490,10 +490,10 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_delete(self)?;
|
||||
let am = ActiveModelBehavior::before_delete(self, db).await?;
|
||||
let am_clone = am.clone();
|
||||
let delete_res = Self::Entity::delete(am).exec(db).await?;
|
||||
ActiveModelBehavior::after_delete(am_clone)?;
|
||||
ActiveModelBehavior::after_delete(am_clone, db).await?;
|
||||
Ok(delete_res)
|
||||
}
|
||||
|
||||
@ -597,6 +597,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
/// ```
|
||||
/// See module level docs [crate::entity] for a full example
|
||||
#[allow(unused_variables)]
|
||||
#[async_trait]
|
||||
pub trait ActiveModelBehavior: ActiveModelTrait {
|
||||
/// Create a new ActiveModel with default values. Also used by `Default::default()`.
|
||||
fn new() -> Self {
|
||||
@ -604,25 +605,38 @@ pub trait ActiveModelBehavior: ActiveModelTrait {
|
||||
}
|
||||
|
||||
/// Will be called before saving
|
||||
fn before_save(self, insert: bool) -> Result<Self, DbErr> {
|
||||
async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Will be called after saving
|
||||
fn after_save(
|
||||
async fn after_save<C>(
|
||||
model: <Self::Entity as EntityTrait>::Model,
|
||||
db: &C,
|
||||
insert: bool,
|
||||
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr> {
|
||||
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
Ok(model)
|
||||
}
|
||||
|
||||
/// Will be called before deleting
|
||||
fn before_delete(self) -> Result<Self, DbErr> {
|
||||
async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Will be called after deleting
|
||||
fn after_delete(self) -> Result<Self, DbErr> {
|
||||
async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{entity::prelude::*, ConnectionTrait};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "cake")]
|
||||
@ -49,6 +49,7 @@ impl Related<super::lineitem::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
fn new() -> Self {
|
||||
use sea_orm::Set;
|
||||
@ -58,7 +59,10 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn before_save(self, insert: bool) -> Result<Self, DbErr> {
|
||||
async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
use rust_decimal_macros::dec;
|
||||
if self.price.as_ref() == &dec!(0) {
|
||||
Err(DbErr::Custom(format!(
|
||||
@ -70,7 +74,10 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn after_save(model: Model, insert: bool) -> Result<Model, DbErr> {
|
||||
async fn after_save<C>(model: Model, _db: &C, insert: bool) -> Result<Model, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
use rust_decimal_macros::dec;
|
||||
if model.price < dec!(0) {
|
||||
Err(DbErr::Custom(format!(
|
||||
@ -82,7 +89,10 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn before_delete(self) -> Result<Self, DbErr> {
|
||||
async fn before_delete<C>(self, _db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
if self.name.as_ref().contains("(err_on_before_delete)") {
|
||||
Err(DbErr::Custom(
|
||||
"[before_delete] Cannot be deleted".to_owned(),
|
||||
@ -92,7 +102,10 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn after_delete(self) -> Result<Self, DbErr> {
|
||||
async fn after_delete<C>(self, _db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
if self.name.as_ref().contains("(err_on_after_delete)") {
|
||||
Err(DbErr::Custom("[after_delete] Cannot be deleted".to_owned()))
|
||||
} else {
|
||||
|
15
tests/common/features/edit_log.rs
Normal file
15
tests/common/features/edit_log.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "edit_log")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub action: String,
|
||||
pub values: Json,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
@ -4,6 +4,7 @@ pub mod applog;
|
||||
pub mod byte_primary_key;
|
||||
pub mod collection;
|
||||
pub mod custom_active_model;
|
||||
pub mod edit_log;
|
||||
pub mod event_trigger;
|
||||
pub mod insert_default;
|
||||
pub mod json_struct;
|
||||
@ -23,6 +24,7 @@ pub use active_enum_child::Entity as ActiveEnumChild;
|
||||
pub use applog::Entity as Applog;
|
||||
pub use byte_primary_key::Entity as BytePrimaryKey;
|
||||
pub use collection::Entity as Collection;
|
||||
pub use edit_log::Entity as EditLog;
|
||||
pub use event_trigger::Entity as EventTrigger;
|
||||
pub use insert_default::Entity as InsertDefault;
|
||||
pub use json_struct::Entity as JsonStruct;
|
||||
|
@ -1,6 +1,8 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use super::edit_log;
|
||||
use sea_orm::{entity::prelude::*, ConnectionTrait, Set, TryIntoModel};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize)]
|
||||
#[sea_orm(table_name = "repository")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
@ -13,4 +15,57 @@ pub struct Model {
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
async fn before_save<C>(self, db: &C, _: bool) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let model = self.clone().try_into_model()?;
|
||||
insert_edit_log("before_save", &model, db).await?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
async fn after_save<C>(model: Model, db: &C, _: bool) -> Result<Model, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
insert_edit_log("after_save", &model, db).await?;
|
||||
Ok(model)
|
||||
}
|
||||
|
||||
async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let model = self.clone().try_into_model()?;
|
||||
insert_edit_log("before_delete", &model, db).await?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let model = self.clone().try_into_model()?;
|
||||
insert_edit_log("after_delete", &model, db).await?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
async fn insert_edit_log<T, M, C>(action: T, model: &M, db: &C) -> Result<(), DbErr>
|
||||
where
|
||||
T: Into<String>,
|
||||
M: Serialize,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
edit_log::ActiveModel {
|
||||
action: Set(action.into()),
|
||||
values: Set(serde_json::json!(model)),
|
||||
..Default::default()
|
||||
}
|
||||
.insert(db)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
create_insert_default_table(db).await?;
|
||||
create_pi_table(db).await?;
|
||||
create_uuid_fmt_table(db).await?;
|
||||
create_edit_log_table(db).await?;
|
||||
|
||||
if DbBackend::Postgres == db_backend {
|
||||
create_collection_table(db).await?;
|
||||
@ -480,3 +481,20 @@ pub async fn create_uuid_fmt_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
|
||||
create_table(db, &stmt, UuidFmt).await
|
||||
}
|
||||
|
||||
pub async fn create_edit_log_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
let stmt = sea_query::Table::create()
|
||||
.table(edit_log::Entity)
|
||||
.col(
|
||||
ColumnDef::new(edit_log::Column::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(edit_log::Column::Action).string().not_null())
|
||||
.col(ColumnDef::new(edit_log::Column::Values).json().not_null())
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt, EditLog).await
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ pub mod common;
|
||||
pub use common::{features::*, setup::*, TestContext};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection};
|
||||
use serde_json::json;
|
||||
|
||||
#[sea_orm_macros::test]
|
||||
#[cfg(any(
|
||||
@ -14,13 +15,13 @@ async fn main() -> Result<(), DbErr> {
|
||||
let ctx = TestContext::new("features_schema_string_primary_key_tests").await;
|
||||
create_tables(&ctx.db).await?;
|
||||
create_and_update_repository(&ctx.db).await?;
|
||||
insert_repository(&ctx.db).await?;
|
||||
insert_and_delete_repository(&ctx.db).await?;
|
||||
ctx.delete().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
pub async fn insert_and_delete_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
let repository = repository::Model {
|
||||
id: "unique-id-001".to_owned(),
|
||||
owner: "GC".to_owned(),
|
||||
@ -41,6 +42,54 @@ pub async fn insert_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
}
|
||||
);
|
||||
|
||||
result.delete(db).await?;
|
||||
|
||||
assert_eq!(
|
||||
edit_log::Entity::find().all(db).await?,
|
||||
[
|
||||
edit_log::Model {
|
||||
id: 1,
|
||||
action: "before_save".into(),
|
||||
values: json!({
|
||||
"description": null,
|
||||
"id": "unique-id-001",
|
||||
"name": "G.C.",
|
||||
"owner": "GC",
|
||||
}),
|
||||
},
|
||||
edit_log::Model {
|
||||
id: 2,
|
||||
action: "after_save".into(),
|
||||
values: json!({
|
||||
"description": null,
|
||||
"id": "unique-id-001",
|
||||
"name": "G.C.",
|
||||
"owner": "GC",
|
||||
}),
|
||||
},
|
||||
edit_log::Model {
|
||||
id: 3,
|
||||
action: "before_delete".into(),
|
||||
values: json!({
|
||||
"description": null,
|
||||
"id": "unique-id-001",
|
||||
"name": "G.C.",
|
||||
"owner": "GC",
|
||||
}),
|
||||
},
|
||||
edit_log::Model {
|
||||
id: 4,
|
||||
action: "after_delete".into(),
|
||||
values: json!({
|
||||
"description": null,
|
||||
"id": "unique-id-001",
|
||||
"name": "G.C.",
|
||||
"owner": "GC",
|
||||
}),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user