Skip to content

Commit

Permalink
Insert & Update Return Model (#339)
Browse files Browse the repository at this point in the history
* Update insert & update API

* Update test cases

* Update README

* Fix clippy warnings

* Fixup

* Fixup
  • Loading branch information
billy1624 authored Dec 18, 2021
1 parent acf8eac commit 5104cd3
Show file tree
Hide file tree
Showing 20 changed files with 160 additions and 145 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ let mut pear: fruit::ActiveModel = pear.unwrap().into();
pear.name = Set("Sweet pear".to_owned());

// update one
let pear: fruit::ActiveModel = pear.update(db).await?;
let pear: fruit::Model = pear.update(db).await?;

// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
Fruit::update_many()
Expand All @@ -142,7 +142,7 @@ let banana = fruit::ActiveModel {
};

// create, because primary key `id` is `Unset`
let mut banana = banana.save(db).await?;
let mut banana = banana.save(db).await?.into_active_model();

banana.name = Set("Banana Mongo".to_owned());

Expand Down
6 changes: 3 additions & 3 deletions examples/basic/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> {
let mut pear: fruit::ActiveModel = pear.unwrap().into();
pear.name = Set("Sweet pear".to_owned());

let pear: fruit::ActiveModel = pear.update(db).await?;
let pear: fruit::Model = pear.update(db).await?;

println!();
println!("Updated: {:?}\n", pear);
Expand All @@ -46,14 +46,14 @@ pub async fn save_active_model(db: &DbConn) -> Result<(), DbErr> {
name: Set("Banana".to_owned()),
..Default::default()
};
let mut banana = banana.save(db).await?;
let mut banana: fruit::ActiveModel = banana.save(db).await?.into_active_model();

println!();
println!("Inserted: {:?}\n", banana);

banana.name = Set("Banana Mongo".to_owned());

let banana = banana.save(db).await?;
let banana: fruit::ActiveModel = banana.save(db).await?.into_active_model();

println!();
println!("Updated: {:?}\n", banana);
Expand Down
32 changes: 15 additions & 17 deletions src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ pub trait ActiveModelTrait: Clone + Debug {
/// id: 15,
/// name: "Apple Pie".to_owned(),
/// }
/// .into_active_model()
/// );
///
/// assert_eq!(
Expand Down Expand Up @@ -225,7 +224,6 @@ pub trait ActiveModelTrait: Clone + Debug {
/// id: 15,
/// name: "Apple Pie".to_owned(),
/// }
/// .into_active_model()
/// );
///
/// assert_eq!(
Expand All @@ -247,17 +245,17 @@ pub trait ActiveModelTrait: Clone + Debug {
/// # Ok(())
/// # }
/// ```
async fn insert<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
async fn insert<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait<'a>,
{
let am = ActiveModelBehavior::before_save(self, true)?;
let am = <Self::Entity as EntityTrait>::insert(am)
let model = <Self::Entity as EntityTrait>::insert(am)
.exec_with_returning(db)
.await?;
ActiveModelBehavior::after_save(am, true)
Self::after_save(model, true)
}

/// Perform the `UPDATE` operation on an ActiveModel
Expand Down Expand Up @@ -296,7 +294,6 @@ pub trait ActiveModelTrait: Clone + Debug {
/// name: "Orange".to_owned(),
/// cake_id: None,
/// }
/// .into_active_model()
/// );
///
/// assert_eq!(
Expand Down Expand Up @@ -351,7 +348,6 @@ pub trait ActiveModelTrait: Clone + Debug {
/// name: "Orange".to_owned(),
/// cake_id: None,
/// }
/// .into_active_model()
/// );
///
/// assert_eq!(
Expand All @@ -371,26 +367,26 @@ pub trait ActiveModelTrait: Clone + Debug {
/// # Ok(())
/// # }
/// ```
async fn update<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
async fn update<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait<'a>,
{
let am = ActiveModelBehavior::before_save(self, false)?;
let am = Self::Entity::update(am).exec(db).await?;
ActiveModelBehavior::after_save(am, false)
let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
Self::after_save(model, 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<Self, DbErr>
async fn save<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait<'a>,
{
let mut am = self;
let am = self;
let mut is_update = true;
for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
let col = key.into_column();
Expand All @@ -400,11 +396,10 @@ pub trait ActiveModelTrait: Clone + Debug {
}
}
if !is_update {
am = am.insert(db).await?;
am.insert(db).await
} else {
am = am.update(db).await?;
am.update(db).await
}
Ok(am)
}

/// Delete an active model by its primary key
Expand Down Expand Up @@ -503,8 +498,11 @@ pub trait ActiveModelBehavior: ActiveModelTrait {
}

/// Will be called after saving
fn after_save(self, insert: bool) -> Result<Self, DbErr> {
Ok(self)
fn after_save(
model: <Self::Entity as EntityTrait>::Model,
insert: bool,
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr> {
Ok(model)
}

/// Will be called before deleting
Expand Down
2 changes: 0 additions & 2 deletions src/entity/base_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ pub trait EntityTrait: EntityName {
/// name: "Orange".to_owned(),
/// cake_id: None,
/// }
/// .into_active_model(),
/// );
///
/// assert_eq!(
Expand Down Expand Up @@ -563,7 +562,6 @@ pub trait EntityTrait: EntityName {
/// name: "Orange".to_owned(),
/// cake_id: None,
/// }
/// .into_active_model(),
/// );
///
/// assert_eq!(
Expand Down
10 changes: 5 additions & 5 deletions src/executor/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ where
pub fn exec_with_returning<'a, C>(
self,
db: &'a C,
) -> impl Future<Output = Result<A, DbErr>> + '_
) -> impl Future<Output = Result<<A::Entity as EntityTrait>::Model, DbErr>> + '_
where
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
C: ConnectionTrait<'a>,
Expand Down Expand Up @@ -92,13 +92,13 @@ where
pub fn exec_with_returning<'a, C>(
self,
db: &'a C,
) -> impl Future<Output = Result<A, DbErr>> + '_
) -> impl Future<Output = Result<<A::Entity as EntityTrait>::Model, DbErr>> + '_
where
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
C: ConnectionTrait<'a>,
A: 'a,
{
exec_insert_with_returning(self.primary_key, self.query, db)
exec_insert_with_returning::<A, _>(self.primary_key, self.query, db)
}
}

Expand Down Expand Up @@ -140,7 +140,7 @@ async fn exec_insert_with_returning<'a, A, C>(
primary_key: Option<ValueTuple>,
mut insert_statement: InsertStatement,
db: &'a C,
) -> Result<A, DbErr>
) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
where
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
C: ConnectionTrait<'a>,
Expand Down Expand Up @@ -175,7 +175,7 @@ where
}
};
match found {
Some(model) => Ok(model.into_active_model()),
Some(model) => Ok(model),
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
}
}
15 changes: 6 additions & 9 deletions src/executor/update.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel,
Iterable, SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne,
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Iterable, SelectModel,
SelectorRaw, Statement, UpdateMany, UpdateOne,
};
use sea_query::{Alias, Expr, FromValueTuple, Query, UpdateStatement};
use std::future::Future;
Expand All @@ -24,9 +24,8 @@ where
A: ActiveModelTrait,
{
/// Execute an update operation on an ActiveModel
pub async fn exec<'b, C>(self, db: &'b C) -> Result<A, DbErr>
pub async fn exec<'b, C>(self, db: &'b C) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
where
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
C: ConnectionTrait<'b>,
{
// so that self is dropped before entering await
Expand Down Expand Up @@ -84,9 +83,8 @@ async fn exec_update_and_return_updated<'a, A, C>(
mut query: UpdateStatement,
model: A,
db: &'a C,
) -> Result<A, DbErr>
) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
where
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
A: ActiveModelTrait,
C: ConnectionTrait<'a>,
{
Expand All @@ -112,7 +110,7 @@ where
.await?;
// If we got `None` then we are updating a row that does not exist.
match found {
Some(model) => Ok(model.into_active_model()),
Some(model) => Ok(model),
None => Err(DbErr::RecordNotFound(
"None of the database rows are affected".to_owned(),
)),
Expand All @@ -130,7 +128,7 @@ where
.await?;
// If we cannot select the updated row from db by the cached primary key
match found {
Some(model) => Ok(model.into_active_model()),
Some(model) => Ok(model),
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
}
}
Expand Down Expand Up @@ -196,7 +194,6 @@ mod tests {
id: 1,
name: "Cheese Cake".to_owned(),
}
.into_active_model()
);

let model = cake::Model {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
//! pear.name = Set("Sweet pear".to_owned());
//!
//! // update one
//! let pear: fruit::ActiveModel = pear.update(db).await?;
//! let pear: fruit::Model = pear.update(db).await?;
//!
//! // update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
//! Fruit::update_many()
Expand All @@ -207,7 +207,7 @@
//! };
//!
//! // create, because primary key `id` is `Unset`
//! let mut banana = banana.save(db).await?;
//! let mut banana = banana.save(db).await?.into_active_model();
//!
//! banana.name = Set("Banana Mongo".to_owned());
//!
Expand Down
36 changes: 18 additions & 18 deletions tests/active_enum_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,25 @@ async fn main() -> Result<(), DbErr> {
pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> {
use active_enum::*;

let am = ActiveModel {
category: Set(None),
color: Set(None),
tea: Set(None),
..Default::default()
}
.insert(db)
.await?;
let model = Model {
id: 1,
category: None,
color: None,
tea: None,
};

let model = Entity::find().one(db).await?.unwrap();
assert_eq!(
model,
Model {
id: 1,
category: None,
color: None,
tea: None,
ActiveModel {
category: Set(None),
color: Set(None),
tea: Set(None),
..Default::default()
}
.insert(db)
.await?
);
assert_eq!(model, Entity::find().one(db).await?.unwrap());
assert_eq!(
model,
Entity::find()
Expand All @@ -58,11 +58,11 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> {
.unwrap()
);

let am = ActiveModel {
let _ = ActiveModel {
category: Set(Some(Category::Big)),
color: Set(Some(Color::Black)),
tea: Set(Some(Tea::EverydayTea)),
..am
..model.into_active_model()
}
.save(db)
.await?;
Expand All @@ -89,7 +89,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> {
.unwrap()
);

let res = am.delete(db).await?;
let res = model.into_active_model().delete(db).await?;

assert_eq!(res.rows_affected, 1);
assert_eq!(Entity::find().one(db).await?, None);
Expand Down Expand Up @@ -146,7 +146,7 @@ pub async fn insert_active_enum_child(db: &DatabaseConnection) -> Result<(), DbE
category: Set(Some(Category::Big)),
color: Set(Some(Color::Black)),
tea: Set(Some(Tea::EverydayTea)),
..am
..am.into_active_model()
}
.save(db)
.await?;
Expand Down
6 changes: 3 additions & 3 deletions tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ async fn crud_cake(db: &DbConn) -> Result<(), DbErr> {
..Default::default()
};

let mut apple = apple.save(db).await?;
let mut apple = apple.save(db).await?.into_active_model();

println!();
println!("Inserted: {:?}", apple);

assert_eq!(
apple,
cake::ActiveModel {
id: Set(1),
name: Set("Apple Pie".to_owned()),
},
apple
}
);

apple.name = Set("Lemon Tart".to_owned());
Expand Down
Loading

0 comments on commit 5104cd3

Please sign in to comment.