Merge branch 'master' into improve-doc-test

This commit is contained in:
Billy Chan 2021-07-12 18:47:25 +08:00 committed by GitHub
commit e65db62e13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1397 additions and 45 deletions

View File

@ -35,6 +35,7 @@ async-stream = { version = "^0.3" }
chrono = { version = "^0", optional = true } chrono = { version = "^0", optional = true }
futures = { version = "^0.3" } futures = { version = "^0.3" }
futures-util = { version = "^0.3" } futures-util = { version = "^0.3" }
rust_decimal = { version = "^1", optional = true }
sea-query = { version = "^0.12" } sea-query = { version = "^0.12" }
sea-orm-macros = { path = "sea-orm-macros", optional = true } sea-orm-macros = { path = "sea-orm-macros", optional = true }
serde = { version = "^1.0", features = [ "derive" ] } serde = { version = "^1.0", features = [ "derive" ] }
@ -45,6 +46,7 @@ serde_json = { version = "^1", optional = true }
[dev-dependencies] [dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] } async-std = { version = "^1.9", features = [ "attributes" ] }
maplit = { version = "^1" } maplit = { version = "^1" }
rust_decimal_macros = { version = "^1" }
sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "sqlx-decimal", "runtime-async-std-native-tls"] } sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "sqlx-decimal", "runtime-async-std-native-tls"] }
[features] [features]
@ -54,7 +56,7 @@ macros = [ "sea-orm-macros" ]
mock = [] mock = []
with-json = [ "serde_json", "sea-query/with-json" ] with-json = [ "serde_json", "sea-query/with-json" ]
with-chrono = [ "chrono", "sea-query/with-chrono" ] with-chrono = [ "chrono", "sea-query/with-chrono" ]
with-rust_decimal = [ "sea-query/with-rust_decimal" ] with-rust_decimal = [ "rust_decimal", "sea-query/with-rust_decimal" ]
sqlx-dep = [ "sqlx" ] sqlx-dep = [ "sqlx" ]
sqlx-json = [ "sqlx/json", "with-json" ] sqlx-json = [ "sqlx/json", "with-json" ]
sqlx-chrono = [ "sqlx/chrono", "with-chrono" ] sqlx-chrono = [ "sqlx/chrono", "with-chrono" ]

View File

@ -116,9 +116,7 @@ impl EntityWriter {
Self::gen_impl_relation_trait(entity), Self::gen_impl_relation_trait(entity),
]; ];
code_blocks.extend(Self::gen_impl_related(entity)); code_blocks.extend(Self::gen_impl_related(entity));
code_blocks.extend(vec![ code_blocks.extend(vec![Self::gen_impl_active_model_behavior()]);
Self::gen_impl_active_model_behavior(),
]);
code_blocks code_blocks
} }

View File

@ -71,7 +71,11 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> { fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> {
match c { match c {
#(<Self::Entity as EntityTrait>::Column::#name => std::mem::take(&mut self.#field).into_wrapped_value(),)* #(<Self::Entity as EntityTrait>::Column::#name => {
let mut value = sea_orm::ActiveValue::unset();
std::mem::swap(&mut value, &mut self.#field);
value.into_wrapped_value()
},)*
_ => sea_orm::ActiveValue::unset(), _ => sea_orm::ActiveValue::unset(),
} }
} }

View File

@ -79,11 +79,7 @@ impl MockDatabaseTrait for MockDatabase {
} }
} }
fn query( fn query(&mut self, counter: usize, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
&mut self,
counter: usize,
statement: Statement,
) -> Result<Vec<QueryResult>, DbErr> {
self.transaction_log.push(Transaction::one(statement)); self.transaction_log.push(Transaction::one(statement));
if counter < self.query_results.len() { if counter < self.query_results.len() {
Ok(std::mem::take(&mut self.query_results[counter]) Ok(std::mem::take(&mut self.query_results[counter])

View File

@ -29,6 +29,9 @@ impl Database {
if crate::MockDatabaseConnector::accepts(string) { if crate::MockDatabaseConnector::accepts(string) {
return crate::MockDatabaseConnector::connect(string).await; return crate::MockDatabaseConnector::connect(string).await;
} }
Err(DbErr::Conn(format!("The connection string '{}' has no supporting driver.", string))) Err(DbErr::Conn(format!(
"The connection string '{}' has no supporting driver.",
string
)))
} }
} }

View File

@ -49,7 +49,9 @@ impl SqlxMySqlPoolConnection {
Err(err) => Err(sqlx_error_to_exec_err(err)), Err(err) => Err(sqlx_error_to_exec_err(err)),
} }
} else { } else {
Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Exec(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
@ -66,7 +68,9 @@ impl SqlxMySqlPoolConnection {
}, },
} }
} else { } else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Query(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
@ -80,7 +84,9 @@ impl SqlxMySqlPoolConnection {
Err(err) => Err(sqlx_error_to_query_err(err)), Err(err) => Err(sqlx_error_to_query_err(err)),
} }
} else { } else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Query(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
} }

View File

@ -49,7 +49,9 @@ impl SqlxSqlitePoolConnection {
Err(err) => Err(sqlx_error_to_exec_err(err)), Err(err) => Err(sqlx_error_to_exec_err(err)),
} }
} else { } else {
Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Exec(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
@ -66,7 +68,9 @@ impl SqlxSqlitePoolConnection {
}, },
} }
} else { } else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Query(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
@ -80,7 +84,9 @@ impl SqlxSqlitePoolConnection {
Err(err) => Err(sqlx_error_to_query_err(err)), Err(err) => Err(sqlx_error_to_query_err(err)),
} }
} else { } else {
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned())) Err(DbErr::Query(
"Failed to acquire connection from pool.".to_owned(),
))
} }
} }
} }

View File

@ -227,7 +227,11 @@ where
let model: Option<E::Model> = found?; let model: Option<E::Model> = found?;
match model { match model {
Some(model) => Ok(model.into_active_model()), Some(model) => Ok(model.into_active_model()),
None => Err(DbErr::Exec(format!("Failed to find inserted item: {} {}", E::default().to_string(), res.last_insert_id))), None => Err(DbErr::Exec(format!(
"Failed to find inserted item: {} {}",
E::default().to_string(),
res.last_insert_id
))),
} }
} else { } else {
Ok(A::default()) Ok(A::default())

View File

@ -1,4 +1,4 @@
use crate::{EntityTrait, DbErr, QueryFilter, QueryResult, Related, Select}; use crate::{DbErr, EntityTrait, QueryFilter, QueryResult, Related, Select};
pub use sea_query::Value; pub use sea_query::Value;
use std::fmt::Debug; use std::fmt::Debug;

View File

@ -62,10 +62,7 @@ async fn exec_delete_only(
} }
// Only Statement impl Send // Only Statement impl Send
async fn exec_delete( async fn exec_delete(statement: Statement, db: &DatabaseConnection) -> Result<DeleteResult, DbErr> {
statement: Statement,
db: &DatabaseConnection,
) -> Result<DeleteResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
Ok(DeleteResult { Ok(DeleteResult {
rows_affected: result.rows_affected(), rows_affected: result.rows_affected(),

View File

@ -40,10 +40,7 @@ impl Inserter {
} }
// Only Statement impl Send // Only Statement impl Send
async fn exec_insert( async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result<InsertResult, DbErr> {
statement: Statement,
db: &DatabaseConnection,
) -> Result<InsertResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
// TODO: Postgres instead use query_one + returning clause // TODO: Postgres instead use query_one + returning clause
Ok(InsertResult { Ok(InsertResult {

View File

@ -1,4 +1,5 @@
use crate::DbErr; use crate::DbErr;
use chrono::NaiveDateTime;
use std::fmt; use std::fmt;
#[derive(Debug)] #[derive(Debug)]
@ -56,12 +57,14 @@ macro_rules! try_getable_all {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => { QueryResultRow::SqlxMySql(row) => {
use sqlx::Row; use sqlx::Row;
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) row.try_get(column.as_str())
.map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(row) => { QueryResultRow::SqlxSqlite(row) => {
use sqlx::Row; use sqlx::Row;
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) row.try_get(column.as_str())
.map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?), QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
@ -109,7 +112,8 @@ macro_rules! try_getable_mysql {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => { QueryResultRow::SqlxMySql(row) => {
use sqlx::Row; use sqlx::Row;
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) row.try_get(column.as_str())
.map_err(crate::sqlx_error_to_query_err)
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(_) => { QueryResultRow::SqlxSqlite(_) => {
@ -160,3 +164,64 @@ try_getable_mysql!(u64);
try_getable_all!(f32); try_getable_all!(f32);
try_getable_all!(f64); try_getable_all!(f64);
try_getable_all!(String); try_getable_all!(String);
try_getable_all!(NaiveDateTime);
#[cfg(feature = "with-rust_decimal")]
use rust_decimal::Decimal;
#[cfg(feature = "with-rust_decimal")]
impl TryGetable for Decimal {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col);
match &res.row {
#[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => {
use sqlx::Row;
row.try_get(column.as_str())
.map_err(crate::sqlx_error_to_query_err)
}
#[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(row) => {
use sqlx::Row;
let val: f64 = row
.try_get(column.as_str())
.map_err(crate::sqlx_error_to_query_err)?;
use rust_decimal::prelude::FromPrimitive;
Decimal::from_f64(val)
.ok_or_else(|| DbErr::Query("Failed to convert f64 into Decimal".to_owned()))
}
#[cfg(feature = "mock")]
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
}
}
}
#[cfg(feature = "with-rust_decimal")]
impl TryGetable for Option<Decimal> {
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
let column = format!("{}{}", pre, col);
match &res.row {
#[cfg(feature = "sqlx-mysql")]
QueryResultRow::SqlxMySql(row) => {
use sqlx::Row;
match row.try_get(column.as_str()) {
Ok(v) => Ok(Some(v)),
Err(_) => Ok(None),
}
}
#[cfg(feature = "sqlx-sqlite")]
QueryResultRow::SqlxSqlite(_) => {
let result: Result<Decimal, _> = TryGetable::try_get(res, pre, col);
match result {
Ok(v) => Ok(Some(v)),
Err(_) => Ok(None),
}
}
#[cfg(feature = "mock")]
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
Ok(v) => Ok(Some(v)),
Err(_) => Ok(None),
},
}
}
}

View File

@ -18,10 +18,7 @@ impl<'a, A: 'a> UpdateOne<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
pub fn exec( pub fn exec(self, db: &'a DatabaseConnection) -> impl Future<Output = Result<A, DbErr>> + 'a {
self,
db: &'a DatabaseConnection,
) -> impl Future<Output = Result<A, DbErr>> + 'a {
// so that self is dropped before entering await // so that self is dropped before entering await
exec_update_and_return_original(self.query, self.model, db) exec_update_and_return_original(self.query, self.model, db)
} }
@ -74,10 +71,7 @@ where
} }
// Only Statement impl Send // Only Statement impl Send
async fn exec_update( async fn exec_update(statement: Statement, db: &DatabaseConnection) -> Result<UpdateResult, DbErr> {
statement: Statement,
db: &DatabaseConnection,
) -> Result<UpdateResult, DbErr> {
let result = db.execute(statement).await?; let result = db.execute(statement).await?;
Ok(UpdateResult { Ok(UpdateResult {
rows_affected: result.rows_affected(), rows_affected: result.rows_affected(),

View File

@ -215,5 +215,5 @@ pub use sea_orm_macros::{
}; };
pub use sea_query; pub use sea_query;
pub use sea_query::Iden; pub use sea_query::Iden;
pub use strum::EnumIter;
pub use strum; pub use strum;
pub use strum::EnumIter;

View File

@ -1,4 +1,4 @@
use crate::{FromQueryResult, DbErr, QueryResult, QueryResultRow}; use crate::{DbErr, FromQueryResult, QueryResult, QueryResultRow};
use serde_json::Map; use serde_json::Map;
pub use serde_json::Value as JsonValue; pub use serde_json::Value as JsonValue;

View File

@ -0,0 +1,81 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"baker"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub bakery_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
BakeryId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Bakery,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
Self::BakeryId => ColumnType::Integer.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
.from(Column::BakeryId)
.to(super::bakery::Column::Id)
.into(),
}
}
}
impl Related<super::bakery::Entity> for Entity {
fn to() -> RelationDef {
Relation::Bakery.def()
}
}
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
super::cakes_bakers::Relation::Cake.def()
}
fn via() -> Option<RelationDef> {
Some(super::cakes_bakers::Relation::Baker.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,84 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"bakery"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub profit_margin: f64,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
ProfitMargin,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Baker,
Order,
Cake,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
Self::ProfitMargin => ColumnType::Float.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Baker => Entity::has_many(super::baker::Entity).into(),
Self::Order => Entity::has_many(super::order::Entity).into(),
Self::Cake => Entity::has_many(super::cake::Entity).into(),
}
}
}
impl Related<super::baker::Entity> for Entity {
fn to() -> RelationDef {
Relation::Baker.def()
}
}
impl Related<super::order::Entity> for Entity {
fn to() -> RelationDef {
Relation::Order.def()
}
}
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
Relation::Cake.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-06-30T11:15:47.471Z" agent="5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="CL5AmeQl6dz-rdIIukNg" version="14.6.13" type="device"><diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">7Zxtc5s4EIB/jT82w5sBf4yd5Nq75K6TdKbtp44Csq0zIB/Itd1ff6sgjI1EYoe3uxnNZDLWWgihZ1fsLmtG9ize/Zai9fKBhjgaWUa4G9k3I8uaOC7854J9LjB9w84li5SEQlYKnsgvLISGkG5IiLOTjozSiJH1qTCgSYIDdiJDaUq3p93mNDo96xotsCR4ClAkS7+SkC1zqT82SvlHTBbL4symIb6JUdFZCLIlCun2RIR37I4mTEzxM05jlOCEwTcPKF3hdDS+XTLGr/R6ZN3B35z3vlpQuogwWpPsKqAxiIMMutzNUUwivs5HA03FQHA6+3Zkz1JKWf4p3s1wxFkVGPI53dV8e1iHlI97xgGr6S77vnKC36eJP/m6/LBa7v/+IEb5iaKNWN/p9R+3j99BNvt4/elPsVJsXyw/gyUa2dMliyMQmPAxYyld4RmNaAqShCbQczonUVQRoYgsEmgGMF9YAHv6E6eMANhr8UVMwpCfZrpdEoaf1ijg59yCGoMspZskxPxSDD48LLtQTdsq2mKSxSXB6HhXu1bmgQBYCqYxZukeuogDLEcohLCSDxNXCLalztmekC2P9K3QQyR0aHEYu0QDHwSdS0iNZVQINGkvIcq2JI5QjuFoXfi6BUsShfdoTzd8uhlDwapoTZc0Jb+gPyrBorRYZcs96fHEjxRjpjiDPp+LtTUroge0O+l4jzJWzIZGEVpn5Flws6cxShckmVLGwIzyTpLaHLE3HWi3gNu2K7h9BW7TUeC2DLcr3q7Em/eHfcf4BCuxyPcQlW2ea5AZWBhJFvd4zi/GKSWP4vq4iMLCzqOXbXIJ9omTF1tkiKHng1atKRF75ngKf7BMM+NqPBrDnGbQNss2/PHuKZvRBKaJyAsfDCqxxVwtTuBa58J9xV5k5AKx5Z4H2O7Mnj2Jb4JinBN+Yilw0IAbAB4PDtiXAD/zDTvTWBtg9YfG6lsSVpqGGmszrKY5NFdzInENwFw11kZYnaGx+vJtdp3SOWE/cl8zv9/eRRQxTboJaW9o0pYilkUrhZus46NL46Oxd058ZCpoHzaA9nHL92EdH11gz3XIX4mPVIC7M2dbx0ddAlbFR/0CdtTxEdE+VyOwqgipX7ByplJ70k2pKgOkfrHKCckZYNXuVXP3qpp+HvsK92qsgG3ZnblXctyk3atLzNm92L1SAe7OmuXspHavWgSscq96BezK+ax1SgJB+AEQyU8ONeDzAavcrF4Be/IO/ax+IKyxno9V6Wf1yrUYWD83apWrKhPdr73KCauIABg4jOFY021EV5V97pWuL0dHi2jDcPJjnmJx051SGmGUaNJNHGdjaNKOvD9LRHG4wEXgi6Nnur0tBdJaHdXS4SS85jWS0Lx9jFGyL+JgSQqLmO6/HQ6DxndOFXCJ5k0R9+atvWjlU+Xzq8UiRBndpAF+jZTI88D8Fvg1pDkKGekxQkOB8CBMcYQY+Xk6ZRVYcY7PXLHL+NqxxldirkWE7RgV9civVhxYaog01lgea1wdK18QaSxgiPZH3YT51U7bs6ppgclJhSd8yIcsdfmwuA3UW75N9afexQ7HZXeET7xtpS2qct5U2jw9PZzS2q5RVTSvuqedq7SOPJZfHatGaVtTK/lxzl+8bEbSLZ0+rFHu2vSh6Vb2CbfICx/rqqXQ1e6qV335JqnThxd4Qbm5XJQ+VAHuzgmqeXinkw9NsKqShv1iVTy622SwnWl7bQRWlSzsFawn2yujLzdLnQ1uA7Aybdiv6coJCJ1eaouuKnnYK11f8TQnQgEOfyCW2/ANYvgLieXn8Rr0BaBVecReQbuy43xfmrGOlhpHS9Jv/WxVsYUysve6ipZcuXRZR0sXWLVbk2B8JVpSAe7O+5LvzYGqdEpDPR+qKlbqFaorJ0p1gUWLgFUxU7+A5ZTlPxuUMML2em9ui7IycOp3c5ZvvjTPTMPMbLN8/qAJv4+wKnjq15Dl5MesLqulHerGDrXy8YPSoe7sx2GunMfUDvUlJl3z8PM/41DLfHX1couAh3eu5YhJv2WhMdbBXWpf8aNOynD+PjXjC0ep+b6f7/DOtCv/rEQi2mb1EN4R9k0Q45+PiuCgVdbA8UYHJXDFNvVmNZFT8/aE3qqJ/IqTZk/eW0tUHcmpjtRxJVHxLoK+Vcy8QMUO1ZmHRu/Vmc65qunVuPdaNS9XzYF2v/+ZahavWnpbNb2BVbP63hPn3RWY1ZHGPddf+nKWq1vVfKeaDXxDL97u9vYNfeDyYKu613nvrWiXVbP6DtuW6tlNKVF0+sbiburZJ5cq/kuC//GFDk0aWECLWumdq5VezY9netswq+9Ntqu6dLZWVkdyztTKt/UGmuVbt/Pu5avS7dt/AQ==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,96 @@
use rust_decimal::prelude::*;
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub price: Decimal,
pub bakery_id: Option<i32>,
pub gluten_free: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
Price,
BakeryId,
GlutenFree,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Bakery,
Lineitem,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
Self::Price => ColumnType::Decimal(Some((19, 4))).def(),
Self::BakeryId => ColumnType::Integer.def(),
Self::GlutenFree => ColumnType::Boolean.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
.from(Column::BakeryId)
.to(super::bakery::Column::Id)
.into(),
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
}
}
}
impl Related<super::bakery::Entity> for Entity {
fn to() -> RelationDef {
Relation::Bakery.def()
}
}
impl Related<super::baker::Entity> for Entity {
fn to() -> RelationDef {
super::cakes_bakers::Relation::Baker.def()
}
fn via() -> Option<RelationDef> {
Some(super::cakes_bakers::Relation::Cake.def().rev())
}
}
impl Related<super::lineitem::Entity> for Entity {
fn to() -> RelationDef {
Relation::Lineitem.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,68 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cakes_bakers"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub cake_id: i32,
pub baker_id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
CakeId,
BakerId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
CakeId,
BakerId,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
false
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Cake,
Baker,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::CakeId => ColumnType::Integer.def(),
Self::BakerId => ColumnType::Integer.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
Self::Baker => Entity::belongs_to(super::baker::Entity)
.from(Column::BakerId)
.to(super::baker::Column::Id)
.into(),
}
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,68 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"customer"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub notes: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
Notes,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Order,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
Self::Notes => ColumnType::Text.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Order => Entity::has_many(super::order::Entity).into(),
}
}
}
impl Related<super::order::Entity> for Entity {
fn to() -> RelationDef {
Relation::Order.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,89 @@
use rust_decimal::prelude::*;
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"lineitem"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub price: Decimal,
pub quantity: i32,
pub order_id: Option<i32>,
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Price,
Quantity,
OrderId,
CakeId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Order,
Cake,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Price => ColumnType::Money(Some((19, 4))).def(),
Self::Quantity => ColumnType::Integer.def(),
Self::OrderId => ColumnType::Integer.def(),
Self::CakeId => ColumnType::Integer.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Order => Entity::belongs_to(super::order::Entity)
.from(Column::OrderId)
.to(super::order::Column::Id)
.into(),
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
}
}
}
impl Related<super::order::Entity> for Entity {
fn to() -> RelationDef {
Relation::Order.def()
}
}
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
Relation::Cake.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

15
tests/bakery_chain/mod.rs Normal file
View File

@ -0,0 +1,15 @@
pub mod baker;
pub mod bakery;
pub mod cake;
pub mod cakes_bakers;
pub mod customer;
pub mod lineitem;
pub mod order;
pub use super::baker::Entity as Baker;
pub use super::bakery::Entity as Bakery;
pub use super::cake::Entity as Cake;
pub use super::cakes_bakers::Entity as CakesBakers;
pub use super::customer::Entity as Customer;
pub use super::lineitem::Entity as Lineitem;
pub use super::order::Entity as Order;

View File

@ -0,0 +1,98 @@
use chrono::NaiveDateTime;
use rust_decimal::prelude::*;
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"order"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub total: Decimal,
pub bakery_id: Option<i32>,
pub customer_id: Option<i32>,
pub placed_at: NaiveDateTime,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Total,
BakeryId,
CustomerId,
PlacedAt,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Bakery,
Customer,
Lineitem,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Total => ColumnType::Decimal(Some((19, 4))).def(),
Self::BakeryId => ColumnType::Integer.def(),
Self::CustomerId => ColumnType::Integer.def(),
Self::PlacedAt => ColumnType::DateTime.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
.from(Column::BakeryId)
.to(super::bakery::Column::Id)
.into(),
Self::Customer => Entity::belongs_to(super::customer::Entity)
.from(Column::CustomerId)
.to(super::customer::Column::Id)
.into(),
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
}
}
}
impl Related<super::bakery::Entity> for Entity {
fn to() -> RelationDef {
Relation::Bakery.def()
}
}
impl Related<super::customer::Entity> for Entity {
fn to() -> RelationDef {
Relation::Customer.def()
}
}
impl Related<super::lineitem::Entity> for Entity {
fn to() -> RelationDef {
Relation::Lineitem.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,34 @@
use sea_orm::DbConn;
pub mod bakery_chain;
mod setup;
pub use bakery_chain::*;
mod crud;
mod schema;
#[async_std::test]
// cargo test --test bakery_chain_tests -- --nocapture
async fn main() {
let db: DbConn = setup::setup().await;
setup_schema(&db).await;
create_entities(&db).await;
}
async fn setup_schema(db: &DbConn) {
assert!(schema::create_bakery_table(db).await.is_ok());
assert!(schema::create_baker_table(db).await.is_ok());
assert!(schema::create_customer_table(db).await.is_ok());
assert!(schema::create_order_table(db).await.is_ok());
assert!(schema::create_lineitem_table(db).await.is_ok());
assert!(schema::create_cake_table(db).await.is_ok());
assert!(schema::create_cakes_bakers_table(db).await.is_ok());
}
async fn create_entities(db: &DbConn) {
crud::test_create_bakery(db).await;
crud::test_create_baker(db).await;
crud::test_create_customer(db).await;
crud::create_cake::test_create_cake(db).await;
crud::create_lineitem::test_create_lineitem(db).await;
crud::create_order::test_create_order(db).await;
}

90
tests/crud/create_cake.rs Normal file
View File

@ -0,0 +1,90 @@
pub use super::*;
use rust_decimal_macros::dec;
pub async fn test_create_cake(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
.exec(db)
.await
.expect("could not insert cake");
let cake: Option<cake::Model> = Cake::find_by_id(cake_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find cake");
let cake_baker = cakes_bakers::ActiveModel {
cake_id: Set(cake_insert_res.last_insert_id as i32),
baker_id: Set(baker_insert_res.last_insert_id as i32),
..Default::default()
};
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
.exec(db)
.await
.expect("could not insert cake_baker");
assert!(cake.is_some());
let cake_model = cake.unwrap();
assert_eq!(cake_model.name, "Mud Cake");
assert_eq!(cake_model.price, dec!(10.25));
assert_eq!(cake_model.gluten_free, false);
assert_eq!(
cake_model
.find_related(Bakery)
.one(db)
.await
.expect("Bakery not found")
.unwrap()
.name,
"SeaSide Bakery"
);
let related_bakers: Vec<baker::Model> = cake_model
.find_related(Baker)
.all(db)
.await
.expect("could not find related bakers");
assert_eq!(related_bakers.len(), 1);
assert_eq!(related_bakers[0].name, "Baker Bob");
let baker: Option<baker::Model> = Baker::find_by_id(baker_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find baker");
let related_cakes: Vec<cake::Model> = baker
.unwrap()
.find_related(Cake)
.all(db)
.await
.expect("could not find related cakes");
assert_eq!(related_cakes.len(), 1);
assert_eq!(related_cakes[0].name, "Mud Cake")
}

View File

@ -0,0 +1,116 @@
pub use super::*;
use chrono::offset::Utc;
use rust_decimal_macros::dec;
pub async fn test_create_lineitem(db: &DbConn) {
// Bakery
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
// Baker
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
// Cake
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
.exec(db)
.await
.expect("could not insert cake");
// Cake_Baker
let cake_baker = cakes_bakers::ActiveModel {
cake_id: Set(cake_insert_res.last_insert_id as i32),
baker_id: Set(baker_insert_res.last_insert_id as i32),
..Default::default()
};
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
.exec(db)
.await
.expect("could not insert cake_baker");
// Customer
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
..Default::default()
};
let customer_insert_res: InsertResult = Customer::insert(customer_kate)
.exec(db)
.await
.expect("could not insert customer");
// Order
let order_1 = order::ActiveModel {
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
};
let order_insert_res: InsertResult = Order::insert(order_1)
.exec(db)
.await
.expect("could not insert order");
// Lineitem
let lineitem_1 = lineitem::ActiveModel {
cake_id: Set(Some(cake_insert_res.last_insert_id as i32)),
order_id: Set(Some(order_insert_res.last_insert_id as i32)),
price: Set(dec!(7.55)),
..Default::default()
};
let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1)
.exec(db)
.await
.expect("could not insert lineitem");
let lineitem: Option<lineitem::Model> =
Lineitem::find_by_id(lineitem_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find lineitem");
assert!(lineitem.is_some());
let lineitem_model = lineitem.unwrap();
assert_eq!(lineitem_model.price, dec!(7.55));
let cake: Option<cake::Model> = Cake::find_by_id(lineitem_model.cake_id)
.one(db)
.await
.expect("could not find cake");
let cake_model = cake.unwrap();
assert_eq!(cake_model.name, "Mud Cake");
let order: Option<order::Model> = Order::find_by_id(lineitem_model.order_id)
.one(db)
.await
.expect("could not find order");
let order_model = order.unwrap();
assert_eq!(
order_model.customer_id.unwrap(),
customer_insert_res.last_insert_id as i32
);
}

123
tests/crud/create_order.rs Normal file
View File

@ -0,0 +1,123 @@
pub use super::*;
use chrono::offset::Utc;
use rust_decimal_macros::dec;
pub async fn test_create_order(db: &DbConn) {
// Bakery
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
// Baker
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
// Cake
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
.exec(db)
.await
.expect("could not insert cake");
// Cake_Baker
let cake_baker = cakes_bakers::ActiveModel {
cake_id: Set(cake_insert_res.last_insert_id as i32),
baker_id: Set(baker_insert_res.last_insert_id as i32),
..Default::default()
};
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
.exec(db)
.await
.expect("could not insert cake_baker");
// Customer
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
..Default::default()
};
let customer_insert_res: InsertResult = Customer::insert(customer_kate)
.exec(db)
.await
.expect("could not insert customer");
// Order
let order_1 = order::ActiveModel {
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
total: Set(dec!(15.10)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
};
let order_insert_res: InsertResult = Order::insert(order_1)
.exec(db)
.await
.expect("could not insert order");
// Lineitem
let lineitem_1 = lineitem::ActiveModel {
cake_id: Set(Some(cake_insert_res.last_insert_id as i32)),
order_id: Set(Some(order_insert_res.last_insert_id as i32)),
price: Set(dec!(7.55)),
quantity: Set(2),
..Default::default()
};
let _lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1)
.exec(db)
.await
.expect("could not insert lineitem");
let order: Option<order::Model> = Order::find_by_id(order_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find order");
assert!(order.is_some());
let order_model = order.unwrap();
assert_eq!(order_model.total, dec!(15.10));
let customer: Option<customer::Model> = Customer::find_by_id(order_model.customer_id)
.one(db)
.await
.expect("could not find customer");
let customer_model = customer.unwrap();
assert_eq!(customer_model.name, "Kate");
let bakery: Option<bakery::Model> = Bakery::find_by_id(order_model.bakery_id)
.one(db)
.await
.expect("could not find bakery");
let bakery_model = bakery.unwrap();
assert_eq!(bakery_model.name, "SeaSide Bakery");
let related_lineitems: Vec<lineitem::Model> = order_model
.find_related(Lineitem)
.all(db)
.await
.expect("could not find related lineitems");
assert_eq!(related_lineitems.len(), 1);
assert_eq!(related_lineitems[0].price, dec!(7.55));
assert_eq!(related_lineitems[0].quantity, 2);
}

106
tests/crud/mod.rs Normal file
View File

@ -0,0 +1,106 @@
use sea_orm::{entity::*, DbConn, InsertResult};
pub use super::bakery_chain::*;
pub mod create_cake;
pub mod create_lineitem;
pub mod create_order;
pub async fn test_create_bakery(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let bakery: Option<bakery::Model> = Bakery::find_by_id(res.last_insert_id)
.one(db)
.await
.expect("could not find bakery");
assert!(bakery.is_some());
let bakery_model = bakery.unwrap();
assert_eq!(bakery_model.name, "SeaSide Bakery");
assert_eq!(bakery_model.profit_margin, 10.4);
}
pub async fn test_create_baker(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
let baker: Option<baker::Model> = Baker::find_by_id(res.last_insert_id)
.one(db)
.await
.expect("could not find baker");
assert!(baker.is_some());
let baker_model = baker.unwrap();
assert_eq!(baker_model.name, "Baker Bob");
assert_eq!(
baker_model
.find_related(Bakery)
.one(db)
.await
.expect("Bakery not found")
.unwrap()
.name,
"SeaSide Bakery"
);
let bakery: Option<bakery::Model> = Bakery::find_by_id(bakery_insert_res.last_insert_id)
.one(db)
.await
.unwrap();
let related_bakers: Vec<baker::Model> = bakery
.unwrap()
.find_related(Baker)
.all(db)
.await
.expect("could not find related bakers");
assert_eq!(related_bakers.len(), 1);
assert_eq!(related_bakers[0].name, "Baker Bob")
}
pub async fn test_create_customer(db: &DbConn) {
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
..Default::default()
};
let res: InsertResult = Customer::insert(customer_kate)
.exec(db)
.await
.expect("could not insert customer");
let customer: Option<customer::Model> = Customer::find_by_id(res.last_insert_id)
.one(db)
.await
.expect("could not find customer");
assert!(customer.is_some());
let customer_model = customer.unwrap();
assert_eq!(customer_model.name, "Kate");
assert_eq!(customer_model.notes, "Loves cheese cake");
}

211
tests/schema/mod.rs Normal file
View File

@ -0,0 +1,211 @@
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement};
pub use super::bakery_chain::*;
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
let builder = db.get_schema_builder_backend();
db.execute(builder.build(stmt)).await
}
pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(bakery::Entity)
.if_not_exists()
.col(
ColumnDef::new(bakery::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(bakery::Column::Name).string())
.col(ColumnDef::new(bakery::Column::ProfitMargin).float())
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(baker::Entity)
.if_not_exists()
.col(
ColumnDef::new(baker::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(baker::Column::Name).string())
.col(ColumnDef::new(baker::Column::BakeryId).integer())
.foreign_key(
ForeignKey::create()
.name("FK_baker_bakery")
.from(baker::Entity, baker::Column::BakeryId)
.to(bakery::Entity, bakery::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(customer::Entity)
.if_not_exists()
.col(
ColumnDef::new(customer::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(customer::Column::Name).string())
.col(ColumnDef::new(customer::Column::Notes).text())
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(order::Entity)
.if_not_exists()
.col(
ColumnDef::new(order::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(order::Column::Total).float())
.col(ColumnDef::new(order::Column::BakeryId).integer().not_null())
.col(
ColumnDef::new(order::Column::CustomerId)
.integer()
.not_null(),
)
.col(
ColumnDef::new(order::Column::PlacedAt)
.date_time()
.not_null(),
)
.foreign_key(
ForeignKey::create()
.name("FK_order_bakery")
.from(order::Entity, order::Column::BakeryId)
.to(bakery::Entity, bakery::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("FK_order_customer")
.from(order::Entity, order::Column::CustomerId)
.to(customer::Entity, customer::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(lineitem::Entity)
.if_not_exists()
.col(
ColumnDef::new(lineitem::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(lineitem::Column::Price).decimal())
.col(ColumnDef::new(lineitem::Column::Quantity).integer())
.col(
ColumnDef::new(lineitem::Column::OrderId)
.integer()
.not_null(),
)
.col(
ColumnDef::new(lineitem::Column::CakeId)
.integer()
.not_null(),
)
.foreign_key(
ForeignKey::create()
.name("FK_lineitem_order")
.from(lineitem::Entity, lineitem::Column::OrderId)
.to(order::Entity, order::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("FK_lineitem_cake")
.from(lineitem::Entity, lineitem::Column::CakeId)
.to(cake::Entity, cake::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(cakes_bakers::Entity)
.if_not_exists()
.col(
ColumnDef::new(cakes_bakers::Column::CakeId)
.integer()
.not_null(),
)
.col(
ColumnDef::new(cakes_bakers::Column::BakerId)
.integer()
.not_null(),
)
.primary_key(
Index::create()
.col(cakes_bakers::Column::CakeId)
.col(cakes_bakers::Column::BakerId),
)
.to_owned();
create_table(db, &stmt).await
}
pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(cake::Entity)
.if_not_exists()
.col(
ColumnDef::new(cake::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(cake::Column::Name).string())
.col(ColumnDef::new(cake::Column::Price).float())
.col(ColumnDef::new(cake::Column::BakeryId).integer().not_null())
.foreign_key(
ForeignKey::create()
.name("FK_cake_bakery")
.from(cake::Entity, cake::Column::BakeryId)
.to(bakery::Entity, bakery::Column::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.col(ColumnDef::new(cake::Column::GlutenFree).boolean())
.to_owned();
create_table(db, &stmt).await
}