diff --git a/README.md b/README.md index 1bd05a58..ae9e7bfc 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,125 @@ SeaORM is a relational ORM to help you build light weight and concurrent web ser Relying on [SQLx](https://github.com/launchbadge/sqlx), SeaORM is a new library with async support from day 1. +```rust +// execute multiple queries in parallel +let cakes_and_fruits: (Vec, Vec) = + futures::try_join!(Cake::find().all(&db), Fruit::find().all(&db))?; + +``` + 2. Dynamic Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex queries without 'fighting the ORM'. +```rust +// build subquery with ease +let cakes_with_filling: Vec = cake::Entity::find() + .filter( + Condition::any().add( + cake::Column::Id.in_subquery( + Query::select() + .column(cake_filling::Column::CakeId) + .from(cake_filling::Entity) + .to_owned(), + ), + ), + ) + .all(&db) + .await?; + +``` + +[more on SeaQuery](https://docs.rs/sea-query/*/sea_query/) + 3. Testable Use mock connections to write unit tests for your logic. +```rust +// Setup mock connection +let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results(vec![ + vec![ + cake::Model { + id: 1, + name: "New York Cheese".to_owned(), + }, + ], + ]) + .into_connection(); + +// Perform your application logic +assert_eq!( + cake::Entity::find().one(&db).await?, + Some(cake::Model { + id: 1, + name: "New York Cheese".to_owned(), + }) +); + +// Compare it against the expected transaction log +assert_eq!( + db.into_transaction_log(), + vec![ + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, + vec![1u64.into()] + ), + ] +); +``` + +[more on testing](/docs/write-test/mock) + 4. Service oriented Quickly build services that join, filter, sort and paginate data in APIs. +```rust +#[get("/?&")] +async fn list( + conn: Connection, + posts_per_page: Option, + page: Option, + flash: Option>, +) -> Template { + // Set page number and items per page + let page = page.unwrap_or(0); + let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); + + // Setup paginator + let paginator = Post::find() + .order_by_asc(post::Column::Id) + .paginate(&conn, posts_per_page); + let num_pages = paginator.num_pages().await.ok().unwrap(); + + // Fetch paginated posts + let posts = paginator + .fetch_page(page) + .await + .expect("could not retrieve posts"); + + let flash = flash.map(FlashMessage::into_inner); + + Template::render( + "index", + context! { + posts: posts, + flash: flash, + page: page, + posts_per_page: posts_per_page, + num_pages: num_pages, + }, + ) +} +``` + +[full Rocket example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example) + +We are building more examples for other web frameworks too. + ## A quick taste of SeaORM ### Select diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 1f6815a0..a031f3c9 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -91,7 +91,9 @@ impl Column { }, ColumnType::DateTime(_) => quote! { ColumnType::DateTime.def() }, ColumnType::Timestamp(_) => quote! { ColumnType::Timestamp.def() }, - ColumnType::TimestampWithTimeZone(_) => quote! { ColumnType::TimestampWithTimeZone.def() }, + ColumnType::TimestampWithTimeZone(_) => { + quote! { ColumnType::TimestampWithTimeZone.def() } + } ColumnType::Time(_) => quote! { ColumnType::Time.def() }, ColumnType::Date => quote! { ColumnType::Date.def() }, ColumnType::Binary(_) => quote! { ColumnType::Binary.def() }, diff --git a/src/entity/column.rs b/src/entity/column.rs index 26d8ec0e..e510756a 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -334,7 +334,7 @@ mod tests { use sea_query::Query; #[test] - fn test_in_subquery() { + fn test_in_subquery_1() { assert_eq!( cake::Entity::find() .filter( @@ -357,6 +357,30 @@ mod tests { ); } + #[test] + fn test_in_subquery_2() { + assert_eq!( + cake::Entity::find() + .filter( + Condition::any().add( + cake::Column::Id.in_subquery( + Query::select() + .column(cake_filling::Column::CakeId) + .from(cake_filling::Entity) + .to_owned() + ) + ) + ) + .build(DbBackend::MySql) + .to_string(), + [ + "SELECT `cake`.`id`, `cake`.`name` FROM `cake`", + "WHERE `cake`.`id` IN (SELECT `cake_id` FROM `cake_filling`)", + ] + .join(" ") + ); + } + #[test] fn test_col_from_str() { use std::str::FromStr; diff --git a/src/lib.rs b/src/lib.rs index 3a281ecd..9fea7569 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,18 +38,138 @@ //! //! Relying on [SQLx](https://github.com/launchbadge/sqlx), SeaORM is a new library with async support from day 1. //! +//! ``` +//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*}; +//! # async fn function(db: &DbConn) -> Result<(), DbErr> { +//! // execute multiple queries in parallel +//! let cakes_and_fruits: (Vec, Vec) = +//! futures::try_join!(Cake::find().all(&db), Fruit::find().all(&db))?; +//! +//! # Ok(()) +//! # } +//! ``` +//! //! 2. Dynamic //! //! Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex queries without 'fighting the ORM'. //! +//! ``` +//! # use sea_query::Query; +//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*}; +//! # async fn function(db: &DbConn) -> Result<(), DbErr> { +//! // build subquery with ease +//! let cakes_with_filling: Vec = cake::Entity::find() +//! .filter( +//! Condition::any().add( +//! cake::Column::Id.in_subquery( +//! Query::select() +//! .column(cake_filling::Column::CakeId) +//! .from(cake_filling::Entity) +//! .to_owned(), +//! ), +//! ), +//! ) +//! .all(&db) +//! .await?; +//! +//! # Ok(()) +//! # } +//! ``` +//! +//! [more on SeaQuery](https://docs.rs/sea-query/*/sea_query/) +//! //! 3. Testable //! //! Use mock connections to write unit tests for your logic. //! +//! ``` +//! # use sea_orm::{error::*, entity::*, query::*, tests_cfg::*, DbConn, MockDatabase, Transaction, DbBackend}; +//! # async fn function(db: &DbConn) -> Result<(), DbErr> { +//! // Setup mock connection +//! let db = MockDatabase::new(DbBackend::Postgres) +//! .append_query_results(vec![ +//! vec![ +//! cake::Model { +//! id: 1, +//! name: "New York Cheese".to_owned(), +//! }, +//! ], +//! ]) +//! .into_connection(); +//! +//! // Perform your application logic +//! assert_eq!( +//! cake::Entity::find().one(&db).await?, +//! Some(cake::Model { +//! id: 1, +//! name: "New York Cheese".to_owned(), +//! }) +//! ); +//! +//! // Compare it against the expected transaction log +//! assert_eq!( +//! db.into_transaction_log(), +//! vec![ +//! Transaction::from_sql_and_values( +//! DbBackend::Postgres, +//! r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, +//! vec![1u64.into()] +//! ), +//! ] +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! [more on testing](/docs/write-test/mock) +//! //! 4. Service oriented //! //! Quickly build services that join, filter, sort and paginate data in APIs. //! +//! ```ignore +//! #[get("/?&")] +//! async fn list( +//! conn: Connection, +//! posts_per_page: Option, +//! page: Option, +//! flash: Option>, +//! ) -> Template { +//! // Set page number and items per page +//! let page = page.unwrap_or(0); +//! let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); +//! +//! // Setup paginator +//! let paginator = Post::find() +//! .order_by_asc(post::Column::Id) +//! .paginate(&conn, posts_per_page); +//! let num_pages = paginator.num_pages().await.ok().unwrap(); +//! +//! // Fetch paginated posts +//! let posts = paginator +//! .fetch_page(page) +//! .await +//! .expect("could not retrieve posts"); +//! +//! let flash = flash.map(FlashMessage::into_inner); +//! +//! Template::render( +//! "index", +//! context! { +//! posts: posts, +//! flash: flash, +//! page: page, +//! posts_per_page: posts_per_page, +//! num_pages: num_pages, +//! }, +//! ) +//! } +//! ``` +//! +//! [full Rocket example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example) +//! +//! We are building more examples for other web frameworks too. +//! //! ## A quick taste of SeaORM //! //! ### Select