From f04bd90328bb6f9fdd7992a2679b78c2533165d4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 18 Oct 2021 15:19:00 +0800 Subject: [PATCH 1/5] Edit insert --- SeaORM/docs/04-basic-crud/02-insert.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/SeaORM/docs/04-basic-crud/02-insert.md b/SeaORM/docs/04-basic-crud/02-insert.md index 10c3caeda2d..c28ef9f50d9 100644 --- a/SeaORM/docs/04-basic-crud/02-insert.md +++ b/SeaORM/docs/04-basic-crud/02-insert.md @@ -34,6 +34,17 @@ assert_eq!(active_model.name, ActiveValue::unchanged("Cheese Cake".to_owned())); ## Insert One +Insert single active model and get inserted `ActiveModel`. + +```rust +let pear = fruit::ActiveModel { + name: Set("Pear".to_owned()), + ..Default::default() // all other attributes are `Unset` +}; + +let res: fruit::ActiveModel = pear.insert(db).await?; +``` + Insert single active model and get the last insert id. ```rust @@ -42,7 +53,7 @@ let pear = fruit::ActiveModel { ..Default::default() // all other attributes are `Unset` }; -let res: InsertResult = pear.insert(db).await?; +let res: InsertResult = fruit::Entity::insert(pear).exec(db).await?; assert_eq!(res.last_insert_id, 28) ``` From 2bae0780e0b09be43c1286964940b1b71519620d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 18 Oct 2021 15:43:20 +0800 Subject: [PATCH 2/5] Transaction (SeaQL/sea-orm#142) --- SeaORM/docs/01-index.md | 2 + .../docs/07-advanced-query/06-transaction.md | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 SeaORM/docs/07-advanced-query/06-transaction.md diff --git a/SeaORM/docs/01-index.md b/SeaORM/docs/01-index.md index 396ead6cfd3..ebc189ee314 100644 --- a/SeaORM/docs/01-index.md +++ b/SeaORM/docs/01-index.md @@ -76,6 +76,8 @@ 6.5 [More joins](/docs/advanced-query/more-join) + 6.6 [Transaction](/docs/advanced-query/transaction) + 7. Internal Design 7.1 [Traits and Types](/docs/internal-design/trait-and-type) diff --git a/SeaORM/docs/07-advanced-query/06-transaction.md b/SeaORM/docs/07-advanced-query/06-transaction.md new file mode 100644 index 00000000000..f9017d4d2d4 --- /dev/null +++ b/SeaORM/docs/07-advanced-query/06-transaction.md @@ -0,0 +1,58 @@ +# Transaction + +You can perform atomic operations inside transaction. There are two transaction APIs available to you. + +## `Closure` style + +Transaction will be committed if the closure returned `Ok`, rollbacked if `Err`. + +```rust +db.transaction::<_, _, DbErr>(|txn| { + Box::pin(async move { + bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + } + .save(txn) + .await?; + + bakery::ActiveModel { + name: Set("Top Bakery".to_owned()), + profit_margin: Set(15.0), + ..Default::default() + } + .save(txn) + .await?; + + Ok(()) + }) +}) +.await; +``` + +## `Begin` ... `commit` / `rollback` style + +`Begin` the transaction followed by `commit` or `rollback`. If `txn` goes out of scope, it'd automatically rollback. + +```rust +let txn = db.begin().await?; + +bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() +} +.save(&txn) +.await?; + +bakery::ActiveModel { + name: Set("Top Bakery".to_owned()), + profit_margin: Set(15.0), + ..Default::default() +} +.save(&txn) +.await?; + +txn.commit().await?; +``` From 5c20262e64abce7d2d2af8430e7bbf815280a361 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 18 Oct 2021 16:24:04 +0800 Subject: [PATCH 3/5] Streaming (SeaQL/sea-orm#222) --- SeaORM/docs/01-index.md | 2 ++ SeaORM/docs/07-advanced-query/07-streaming.md | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 SeaORM/docs/07-advanced-query/07-streaming.md diff --git a/SeaORM/docs/01-index.md b/SeaORM/docs/01-index.md index ebc189ee314..c481735ae9a 100644 --- a/SeaORM/docs/01-index.md +++ b/SeaORM/docs/01-index.md @@ -78,6 +78,8 @@ 6.6 [Transaction](/docs/advanced-query/transaction) + 6.7 [Streaming](/docs/advanced-query/streaming) + 7. Internal Design 7.1 [Traits and Types](/docs/internal-design/trait-and-type) diff --git a/SeaORM/docs/07-advanced-query/07-streaming.md b/SeaORM/docs/07-advanced-query/07-streaming.md new file mode 100644 index 00000000000..080dd5f15d3 --- /dev/null +++ b/SeaORM/docs/07-advanced-query/07-streaming.md @@ -0,0 +1,34 @@ +# Streaming + +Use async stream on any `Select` for memory efficiency. + +```rust +// Stream all fruits +let mut stream = Fruit::find().stream(db).await?; + +while let Some(item) = stream.try_next().await? { + let item: fruit::ActiveModel = item.into(); + // do something with item +} +``` + +```rust +// Stream all fruits with name contains character "a" +let mut stream = Fruit::find() + .filter(fruit::Column::Name.contains("a")) + .order_by_asc(fruit::Column::Name) + .stream(db) + .await?; +``` + +Note that stream will persists the connection from connection pool until it gets dropped. + +```rust +{ + // 3 connections are used + let _ = Fruit::find().stream(db).await?; + let _ = Fruit::find().stream(db).await?; + let _ = Fruit::find().stream(db).await?; +} +// All streams are dropped and connections are returned to connection pool +``` From 24f63c9b822996c1c8004c82d0d82e2389ac0a8c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 18 Oct 2021 16:31:35 +0800 Subject: [PATCH 4/5] ActiveModelBehavior (SeaQL/sea-orm#161) --- .../03-expanded-entity-structure.md | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/SeaORM/docs/03-generate-entity/03-expanded-entity-structure.md b/SeaORM/docs/03-generate-entity/03-expanded-entity-structure.md index ff305608ccf..55533e8a3fb 100644 --- a/SeaORM/docs/03-generate-entity/03-expanded-entity-structure.md +++ b/SeaORM/docs/03-generate-entity/03-expanded-entity-structure.md @@ -153,22 +153,37 @@ Handler for different actions on an `ActiveModel`. impl ActiveModelBehavior for ActiveModel { /// Create a new ActiveModel with default values. Also used by `Default::default()`. fn new() -> Self { - ::default() + Self { + uuid: Set(Uuid::new_v4()), + ..ActiveModelTrait::default() + } + } + + /// Will be triggered before insert / update + fn before_save(self, insert: bool) -> Result { + if self.price.as_ref() <= &0.0 { + Err(DbErr::Custom(format!( + "[before_save] Invalid Price, insert: {}", + insert + ))) + } else { + Ok(self) + } } - /// Will be called before saving - fn before_save(self) -> Self { - self + /// Will be triggered after insert / update + fn after_save(self, insert: bool) -> Result { + Ok(self) } - /// Will be called after saving - fn after_save(self) -> Self { - self + /// Will be triggered before delete + fn before_delete(self) -> Result { + Ok(self) } - /// Will be called before deleting - fn before_delete(self) -> Self { - self + /// Will be triggered after delete + fn after_delete(self) -> Result { + Ok(self) } } ``` From 45f004fccef005a17990648f5576dc03e4cca1a4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 18 Oct 2021 17:00:09 +0800 Subject: [PATCH 5/5] DeriveActiveModel (SeaQL/sea-orm#240) --- SeaORM/docs/01-index.md | 2 + .../08-custom-active-model.md | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 SeaORM/docs/07-advanced-query/08-custom-active-model.md diff --git a/SeaORM/docs/01-index.md b/SeaORM/docs/01-index.md index c481735ae9a..f3b4aac689d 100644 --- a/SeaORM/docs/01-index.md +++ b/SeaORM/docs/01-index.md @@ -80,6 +80,8 @@ 6.7 [Streaming](/docs/advanced-query/streaming) + 6.8 [Custom Active Model](/docs/advanced-query/custom-active-model) + 7. Internal Design 7.1 [Traits and Types](/docs/internal-design/trait-and-type) diff --git a/SeaORM/docs/07-advanced-query/08-custom-active-model.md b/SeaORM/docs/07-advanced-query/08-custom-active-model.md new file mode 100644 index 00000000000..87c12ed87f5 --- /dev/null +++ b/SeaORM/docs/07-advanced-query/08-custom-active-model.md @@ -0,0 +1,87 @@ +# Custom Active Model + +A derive macro `DeriveIntoActiveModel` for implementing `IntoActiveModel` on structs. This is useful for creating your own struct with only partial fields of a model, for example an insert struct, or update struct. + +`IntoActiveValue` trait allows converting `Option` into `ActiveValue` automatically. + +```rust +// Define regular model as usual +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "fruit")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub cake_id: Option, +} +``` + +Create a new struct with some fields omitted. + +```rust +#[derive(DeriveIntoActiveModel)] +pub struct NewFruit { + // id is omitted + pub name: String, + // it is required as opposed to optional in Model + pub cake_id: i32, +} + +assert_eq!( + NewFruit { + name: "Apple".to_owned(), + cake_id: 1, + } + .into_active_model(), + fruit::ActiveModel { + id: Unset(None), + name: Set("Apple".to_owned()), + cake_id: Set(Some(1)), + } +); +``` + +`Option>` allows for `Some(None)` to update the column to be NULL. + +```rust +#[derive(DeriveIntoActiveModel)] +pub struct UpdateFruit { + pub cake_id: Option>, +} + +assert_eq!( + UpdateFruit { + cake_id: Some(Some(1)), + } + .into_active_model(), + fruit::ActiveModel { + id: Unset(None), + name: Unset(None), + cake_id: Set(Some(1)), + } +); + +assert_eq!( + UpdateFruit { + cake_id: Some(None), + } + .into_active_model(), + fruit::ActiveModel { + id: Unset(None), + name: Unset(None), + cake_id: Set(None), + } +); + +assert_eq!( + UpdateFruit { + cake_id: None, + } + .into_active_model(), + fruit::ActiveModel { + id: Unset(None), + name: Unset(None), + cake_id: Unset(None), + } +); +```