diff --git a/SeaORM/docs/01-index.md b/SeaORM/docs/01-index.md index 396ead6cfd3..f3b4aac689d 100644 --- a/SeaORM/docs/01-index.md +++ b/SeaORM/docs/01-index.md @@ -76,6 +76,12 @@ 6.5 [More joins](/docs/advanced-query/more-join) + 6.6 [Transaction](/docs/advanced-query/transaction) + + 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/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) } } ``` 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) ``` 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?; +``` 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 +``` 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), + } +); +```