Merge branch 'master' into ss/test_suite
# Conflicts: # Cargo.toml
This commit is contained in:
commit
2e0f6df743
1
.github/workflows/rust.yml
vendored
1
.github/workflows/rust.yml
vendored
@ -31,3 +31,4 @@ jobs:
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all
|
||||
|
44
Cargo.toml
44
Cargo.toml
@ -37,23 +37,27 @@ name = "sea_orm"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
async-stream = { version="^0.3" }
|
||||
chrono = { version="^0", optional=true }
|
||||
futures = { version="^0.3" }
|
||||
futures-util = { version="^0.3" }
|
||||
rust_decimal = { version="^1", optional=true }
|
||||
sea-query = { version="^0.12" }
|
||||
sea-orm-macros = { path="sea-orm-macros", optional=true }
|
||||
serde = { version="^1.0", features=["derive"] }
|
||||
sqlx = { version="^0.5", optional=true }
|
||||
strum = { git="https://github.com/SeaQL/strum.git", branch="sea-orm", version="^0.21", features=["derive", "sea-orm"] }
|
||||
serde_json = { version="^1", optional=true }
|
||||
async-stream = { version = "^0.3" }
|
||||
chrono = { version = "^0", optional = true }
|
||||
futures = { version = "^0.3" }
|
||||
futures-util = { version = "^0.3" }
|
||||
rust_decimal = { version = "^1", optional = true }
|
||||
sea-query = { version = "^0.12" }
|
||||
sea-orm-macros = { path = "sea-orm-macros", optional = true }
|
||||
sea-orm-codegen = { path = "sea-orm-codegen", optional = true }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
sqlx = { version = "^0.5", optional = true }
|
||||
strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [
|
||||
"derive",
|
||||
"sea-orm",
|
||||
] }
|
||||
serde_json = { version = "^1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
async-std = { version="^1.9", features=["attributes"] }
|
||||
maplit = { version="^1" }
|
||||
rust_decimal_macros = { version="^1" }
|
||||
sea-orm = { path=".", features=[
|
||||
async-std = { version = "^1.9", features = ["attributes"] }
|
||||
maplit = { version = "^1" }
|
||||
rust_decimal_macros = { version = "^1" }
|
||||
sea-orm = { path = ".", features = [
|
||||
"sqlx-sqlite",
|
||||
"sqlx-json",
|
||||
"sqlx-chrono",
|
||||
@ -64,8 +68,16 @@ sea-orm = { path=".", features=[
|
||||
|
||||
[features]
|
||||
debug-print = []
|
||||
default = ["macros", "with-json", "with-chrono", "with-rust_decimal", "mock"]
|
||||
default = [
|
||||
"macros",
|
||||
"codegen",
|
||||
"with-json",
|
||||
"with-chrono",
|
||||
"with-rust_decimal",
|
||||
"mock",
|
||||
]
|
||||
macros = ["sea-orm-macros"]
|
||||
codegen = ["sea-orm-codegen"]
|
||||
mock = []
|
||||
with-json = ["serde_json", "sea-query/with-json"]
|
||||
with-chrono = ["chrono", "sea-query/with-chrono"]
|
||||
|
@ -23,3 +23,7 @@ syn = { version = "^1", default-features = false, features = [ "derive", "parsin
|
||||
quote = "^1"
|
||||
heck = "^0.3"
|
||||
proc-macro2 = "^1"
|
||||
|
||||
[dev-dependencies]
|
||||
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||
sea-orm = { path = "../", features = ["mock", "sqlx-json", "sqlx-chrono", "runtime-async-std-native-tls"] }
|
||||
|
@ -22,7 +22,7 @@ impl Column {
|
||||
}
|
||||
|
||||
pub fn get_rs_type(&self) -> TokenStream {
|
||||
let ident = match self.col_type {
|
||||
let ident: TokenStream = match self.col_type {
|
||||
ColumnType::Char(_)
|
||||
| ColumnType::String(_)
|
||||
| ColumnType::Text
|
||||
@ -32,18 +32,18 @@ impl Column {
|
||||
| ColumnType::Date
|
||||
| ColumnType::Json
|
||||
| ColumnType::JsonBinary
|
||||
| ColumnType::Custom(_) => format_ident!("String"),
|
||||
ColumnType::TinyInteger(_) => format_ident!("i8"),
|
||||
ColumnType::SmallInteger(_) => format_ident!("i16"),
|
||||
ColumnType::Integer(_) => format_ident!("i32"),
|
||||
ColumnType::BigInteger(_) => format_ident!("i64"),
|
||||
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => {
|
||||
format_ident!("f32")
|
||||
| ColumnType::Custom(_) => "String",
|
||||
ColumnType::TinyInteger(_) => "i8",
|
||||
ColumnType::SmallInteger(_) => "i16",
|
||||
ColumnType::Integer(_) => "i32",
|
||||
ColumnType::BigInteger(_) => "i64",
|
||||
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => "f32",
|
||||
ColumnType::Double(_) => "f64",
|
||||
ColumnType::Binary(_) => "Vec<u8>",
|
||||
ColumnType::Boolean => "bool",
|
||||
}
|
||||
ColumnType::Double(_) => format_ident!("f64"),
|
||||
ColumnType::Binary(_) => format_ident!("Vec<u8>"),
|
||||
ColumnType::Boolean => format_ident!("bool"),
|
||||
};
|
||||
.parse()
|
||||
.unwrap();
|
||||
match self.not_null {
|
||||
true => quote! { #ident },
|
||||
false => quote! { Option<#ident> },
|
||||
@ -102,6 +102,12 @@ impl Column {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColumnDef> for Column {
|
||||
fn from(col_def: ColumnDef) -> Self {
|
||||
(&col_def).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ColumnDef> for Column {
|
||||
fn from(col_def: &ColumnDef) -> Self {
|
||||
let name = col_def.get_column_name();
|
||||
@ -145,3 +151,164 @@ impl From<&ColumnDef> for Column {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Column;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use sea_query::{Alias, ColumnDef, ColumnType, SeaRc};
|
||||
|
||||
fn setup() -> Vec<Column> {
|
||||
macro_rules! make_col {
|
||||
($name:expr, $col_type:expr) => {
|
||||
Column {
|
||||
name: $name.to_owned(),
|
||||
col_type: $col_type,
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
}
|
||||
};
|
||||
}
|
||||
vec![
|
||||
make_col!("id", ColumnType::String(Some(255))),
|
||||
make_col!(
|
||||
"cake_id",
|
||||
ColumnType::Custom(SeaRc::new(Alias::new("cus_col")))
|
||||
),
|
||||
make_col!("CakeId", ColumnType::TinyInteger(None)),
|
||||
make_col!("CakeId", ColumnType::SmallInteger(None)),
|
||||
make_col!("CakeId", ColumnType::Integer(Some(11))),
|
||||
make_col!("CakeFillingId", ColumnType::BigInteger(None)),
|
||||
make_col!("cake-filling-id", ColumnType::Float(None)),
|
||||
make_col!("CAKE_FILLING_ID", ColumnType::Double(None)),
|
||||
make_col!("CAKE-FILLING-ID", ColumnType::Binary(None)),
|
||||
make_col!("CAKE", ColumnType::Boolean),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_name_snake_case() {
|
||||
let columns = setup();
|
||||
let snack_cases = vec![
|
||||
"id",
|
||||
"cake_id",
|
||||
"cake_id",
|
||||
"cake_id",
|
||||
"cake_id",
|
||||
"cake_filling_id",
|
||||
"cake_filling_id",
|
||||
"cake_filling_id",
|
||||
"cake_filling_id",
|
||||
"cake",
|
||||
];
|
||||
for (col, snack_case) in columns.into_iter().zip(snack_cases) {
|
||||
assert_eq!(col.get_name_snake_case().to_string(), snack_case);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_name_camel_case() {
|
||||
let columns = setup();
|
||||
let camel_cases = vec![
|
||||
"Id",
|
||||
"CakeId",
|
||||
"CakeId",
|
||||
"CakeId",
|
||||
"CakeId",
|
||||
"CakeFillingId",
|
||||
"CakeFillingId",
|
||||
"CakeFillingId",
|
||||
"CakeFillingId",
|
||||
"Cake",
|
||||
];
|
||||
for (col, camel_case) in columns.into_iter().zip(camel_cases) {
|
||||
assert_eq!(col.get_name_camel_case().to_string(), camel_case);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rs_type() {
|
||||
let columns = setup();
|
||||
let rs_types = vec![
|
||||
"String", "String", "i8", "i16", "i32", "i64", "f32", "f64", "Vec<u8>", "bool",
|
||||
];
|
||||
for (mut col, rs_type) in columns.into_iter().zip(rs_types) {
|
||||
let rs_type: TokenStream = rs_type.parse().unwrap();
|
||||
|
||||
col.not_null = true;
|
||||
assert_eq!(col.get_rs_type().to_string(), quote!(#rs_type).to_string());
|
||||
|
||||
col.not_null = false;
|
||||
assert_eq!(
|
||||
col.get_rs_type().to_string(),
|
||||
quote!(Option<#rs_type>).to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_def() {
|
||||
let columns = setup();
|
||||
let col_defs = vec![
|
||||
"ColumnType::String(Some(255u32)).def()",
|
||||
"ColumnType::Custom(\"cus_col\".to_owned()).def()",
|
||||
"ColumnType::TinyInteger.def()",
|
||||
"ColumnType::SmallInteger.def()",
|
||||
"ColumnType::Integer.def()",
|
||||
"ColumnType::BigInteger.def()",
|
||||
"ColumnType::Float.def()",
|
||||
"ColumnType::Double.def()",
|
||||
"ColumnType::Binary.def()",
|
||||
"ColumnType::Boolean.def()",
|
||||
];
|
||||
for (mut col, col_def) in columns.into_iter().zip(col_defs) {
|
||||
let mut col_def: TokenStream = col_def.parse().unwrap();
|
||||
|
||||
col.not_null = true;
|
||||
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||
|
||||
col.not_null = false;
|
||||
col_def.extend(quote!(.null()));
|
||||
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||
|
||||
col.unique = true;
|
||||
col_def.extend(quote!(.unique()));
|
||||
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_column_def() {
|
||||
let column: Column = ColumnDef::new(Alias::new("id")).string().into();
|
||||
assert_eq!(
|
||||
column.get_def().to_string(),
|
||||
quote! {
|
||||
ColumnType::String(None).def().null()
|
||||
}
|
||||
.to_string()
|
||||
);
|
||||
|
||||
let column: Column = ColumnDef::new(Alias::new("id")).string().not_null().into();
|
||||
assert!(column.not_null);
|
||||
|
||||
let column: Column = ColumnDef::new(Alias::new("id"))
|
||||
.string()
|
||||
.unique_key()
|
||||
.not_null()
|
||||
.into();
|
||||
assert!(column.unique);
|
||||
assert!(column.not_null);
|
||||
|
||||
let column: Column = ColumnDef::new(Alias::new("id"))
|
||||
.string()
|
||||
.auto_increment()
|
||||
.unique_key()
|
||||
.not_null()
|
||||
.into();
|
||||
assert!(column.auto_increment);
|
||||
assert!(column.unique);
|
||||
assert!(column.not_null);
|
||||
}
|
||||
}
|
||||
|
@ -111,15 +111,242 @@ impl Entity {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_relation_rel_find_helpers(&self) -> Vec<Ident> {
|
||||
self.relations
|
||||
.iter()
|
||||
.map(|rel| rel.get_rel_find_helper())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_primary_key_auto_increment(&self) -> Ident {
|
||||
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
|
||||
format_ident!("{}", auto_increment)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Column, Entity, PrimaryKey, Relation, RelationType};
|
||||
use quote::format_ident;
|
||||
use sea_query::ColumnType;
|
||||
|
||||
fn setup() -> Entity {
|
||||
Entity {
|
||||
table_name: "special_cake".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::String(None),
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "name".to_owned(),
|
||||
col_type: ColumnType::String(None),
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![
|
||||
Relation {
|
||||
ref_table: "fruit".to_owned(),
|
||||
columns: vec!["id".to_owned()],
|
||||
ref_columns: vec!["cake_id".to_owned()],
|
||||
rel_type: RelationType::HasOne,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "filling".to_owned(),
|
||||
columns: vec!["id".to_owned()],
|
||||
ref_columns: vec!["cake_id".to_owned()],
|
||||
rel_type: RelationType::HasOne,
|
||||
},
|
||||
],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_table_name_snake_case() {
|
||||
let entity = setup();
|
||||
|
||||
assert_eq!(
|
||||
entity.get_table_name_snake_case(),
|
||||
"special_cake".to_owned()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_table_name_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
assert_eq!(entity.get_table_name_camel_case(), "SpecialCake".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_table_name_snake_case_ident() {
|
||||
let entity = setup();
|
||||
|
||||
assert_eq!(
|
||||
entity.get_table_name_snake_case_ident(),
|
||||
format_ident!("{}", "special_cake")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_table_name_camel_case_ident() {
|
||||
let entity = setup();
|
||||
|
||||
assert_eq!(
|
||||
entity.get_table_name_camel_case_ident(),
|
||||
format_ident!("{}", "SpecialCake")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_column_names_snake_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_column_names_snake_case().into_iter().enumerate() {
|
||||
assert_eq!(elem, entity.columns[i].get_name_snake_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_column_names_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_column_names_camel_case().into_iter().enumerate() {
|
||||
assert_eq!(elem, entity.columns[i].get_name_camel_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_column_rs_types() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_column_rs_types().into_iter().enumerate() {
|
||||
assert_eq!(
|
||||
elem.to_string(),
|
||||
entity.columns[i].get_rs_type().to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_column_defs() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_column_defs().into_iter().enumerate() {
|
||||
assert_eq!(elem.to_string(), entity.columns[i].get_def().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_primary_key_names_snake_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_primary_key_names_snake_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.primary_keys[i].get_name_snake_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_primary_key_names_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_primary_key_names_camel_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.primary_keys[i].get_name_camel_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_ref_tables_snake_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_relation_ref_tables_snake_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.relations[i].get_ref_table_snake_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_ref_tables_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_relation_ref_tables_camel_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.relations[i].get_ref_table_camel_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_defs() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_relation_defs().into_iter().enumerate() {
|
||||
assert_eq!(elem.to_string(), entity.relations[i].get_def().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_rel_types() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity.get_relation_rel_types().into_iter().enumerate() {
|
||||
assert_eq!(elem, entity.relations[i].get_rel_type());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_columns_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_relation_columns_camel_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.relations[i].get_column_camel_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_relation_ref_columns_camel_case() {
|
||||
let entity = setup();
|
||||
|
||||
for (i, elem) in entity
|
||||
.get_relation_ref_columns_camel_case()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(elem, entity.relations[i].get_ref_column_camel_case());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_primary_key_auto_increment() {
|
||||
let mut entity = setup();
|
||||
|
||||
assert_eq!(
|
||||
entity.get_primary_key_auto_increment(),
|
||||
format_ident!("{}", false)
|
||||
);
|
||||
|
||||
entity.columns[0].auto_increment = true;
|
||||
assert_eq!(
|
||||
entity.get_primary_key_auto_increment(),
|
||||
format_ident!("{}", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,3 +16,28 @@ impl PrimaryKey {
|
||||
format_ident!("{}", self.name.to_camel_case())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::PrimaryKey;
|
||||
|
||||
fn setup() -> PrimaryKey {
|
||||
PrimaryKey {
|
||||
name: "cake_id".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_name_snake_case() {
|
||||
let primary_key = setup();
|
||||
|
||||
assert_eq!(primary_key.get_name_snake_case(), "cake_id".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_name_camel_case() {
|
||||
let primary_key = setup();
|
||||
|
||||
assert_eq!(primary_key.get_name_camel_case(), "CakeId".to_owned());
|
||||
}
|
||||
}
|
||||
|
@ -64,10 +64,6 @@ impl Relation {
|
||||
pub fn get_ref_column_camel_case(&self) -> Ident {
|
||||
format_ident!("{}", self.ref_columns[0].to_camel_case())
|
||||
}
|
||||
|
||||
pub fn get_rel_find_helper(&self) -> Ident {
|
||||
format_ident!("find_{}", self.ref_table.to_snake_case())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TableForeignKey> for Relation {
|
||||
@ -87,3 +83,95 @@ impl From<&TableForeignKey> for Relation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Relation, RelationType};
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
fn setup() -> Vec<Relation> {
|
||||
vec![
|
||||
Relation {
|
||||
ref_table: "fruit".to_owned(),
|
||||
columns: vec!["id".to_owned()],
|
||||
ref_columns: vec!["cake_id".to_owned()],
|
||||
rel_type: RelationType::HasOne,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "filling".to_owned(),
|
||||
columns: vec!["filling_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::BelongsTo,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "filling".to_owned(),
|
||||
columns: vec!["filling_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::HasMany,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ref_table_snake_case() {
|
||||
let relations = setup();
|
||||
let snake_cases = vec!["fruit", "filling", "filling"];
|
||||
for (rel, snake_case) in relations.into_iter().zip(snake_cases) {
|
||||
assert_eq!(rel.get_ref_table_snake_case().to_string(), snake_case);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ref_table_camel_case() {
|
||||
let relations = setup();
|
||||
let camel_cases = vec!["Fruit", "Filling", "Filling"];
|
||||
for (rel, camel_case) in relations.into_iter().zip(camel_cases) {
|
||||
assert_eq!(rel.get_ref_table_camel_case().to_string(), camel_case);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_def() {
|
||||
let relations = setup();
|
||||
let rel_defs = vec![
|
||||
"Entity::has_one(super::fruit::Entity).into()",
|
||||
"Entity::belongs_to(super::filling::Entity) \
|
||||
.from(Column::FillingId) \
|
||||
.to(super::filling::Column::Id) \
|
||||
.into()",
|
||||
"Entity::has_many(super::filling::Entity).into()",
|
||||
];
|
||||
for (rel, rel_def) in relations.into_iter().zip(rel_defs) {
|
||||
let rel_def: TokenStream = rel_def.parse().unwrap();
|
||||
|
||||
assert_eq!(rel.get_def().to_string(), rel_def.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rel_type() {
|
||||
let relations = setup();
|
||||
let rel_types = vec!["has_one", "belongs_to", "has_many"];
|
||||
for (rel, rel_type) in relations.into_iter().zip(rel_types) {
|
||||
assert_eq!(rel.get_rel_type(), rel_type);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_column_camel_case() {
|
||||
let relations = setup();
|
||||
let cols = vec!["Id", "FillingId", "FillingId"];
|
||||
for (rel, col) in relations.into_iter().zip(cols) {
|
||||
assert_eq!(rel.get_column_camel_case(), col);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ref_column_camel_case() {
|
||||
let relations = setup();
|
||||
let ref_cols = vec!["CakeId", "Id", "Id"];
|
||||
for (rel, ref_col) in relations.into_iter().zip(ref_cols) {
|
||||
assert_eq!(rel.get_ref_column_camel_case(), ref_col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ impl EntityTransformer {
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{:#?}", entities);
|
||||
Ok(EntityWriter { entities })
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +116,7 @@ impl EntityWriter {
|
||||
Self::gen_impl_relation_trait(entity),
|
||||
];
|
||||
code_blocks.extend(Self::gen_impl_related(entity));
|
||||
code_blocks.extend(vec![
|
||||
Self::gen_impl_active_model_behavior(),
|
||||
]);
|
||||
code_blocks.extend(vec![Self::gen_impl_active_model_behavior()]);
|
||||
code_blocks
|
||||
}
|
||||
|
||||
@ -152,7 +150,7 @@ impl EntityWriter {
|
||||
quote! {
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||
pub struct Model {
|
||||
#(pub #column_names_snake_case: #column_rs_types),*
|
||||
#(pub #column_names_snake_case: #column_rs_types,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +160,7 @@ impl EntityWriter {
|
||||
quote! {
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
#(#column_names_camel_case),*
|
||||
#(#column_names_camel_case,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,7 +170,7 @@ impl EntityWriter {
|
||||
quote! {
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||
pub enum PrimaryKey {
|
||||
#(#primary_key_names_camel_case),*
|
||||
#(#primary_key_names_camel_case,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,7 +191,7 @@ impl EntityWriter {
|
||||
quote! {
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
pub enum Relation {
|
||||
#(#relation_ref_tables_camel_case),*
|
||||
#(#relation_ref_tables_camel_case,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +205,7 @@ impl EntityWriter {
|
||||
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
#(Self::#column_names_camel_case => #column_defs),*
|
||||
#(Self::#column_names_camel_case => #column_defs,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +221,7 @@ impl EntityWriter {
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#(Self::#relation_ref_tables_camel_case => #relation_defs),*
|
||||
#(Self::#relation_ref_tables_camel_case => #relation_defs,)*
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
@ -276,3 +274,239 @@ impl EntityWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Column, Entity, EntityWriter, PrimaryKey, Relation, RelationType};
|
||||
use proc_macro2::TokenStream;
|
||||
use sea_query::ColumnType;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
|
||||
const ENTITY_FILES: [&'static str; 5] = [
|
||||
include_str!("../../tests/entity/cake.rs"),
|
||||
include_str!("../../tests/entity/cake_filling.rs"),
|
||||
include_str!("../../tests/entity/filling.rs"),
|
||||
include_str!("../../tests/entity/fruit.rs"),
|
||||
include_str!("../../tests/entity/vendor.rs"),
|
||||
];
|
||||
|
||||
fn setup() -> Vec<Entity> {
|
||||
vec![
|
||||
Entity {
|
||||
table_name: "cake".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: true,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "name".to_owned(),
|
||||
col_type: ColumnType::String(Some(255)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![
|
||||
Relation {
|
||||
ref_table: "cake_filling".to_owned(),
|
||||
columns: vec![],
|
||||
ref_columns: vec![],
|
||||
rel_type: RelationType::HasMany,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "fruit".to_owned(),
|
||||
columns: vec![],
|
||||
ref_columns: vec![],
|
||||
rel_type: RelationType::HasMany,
|
||||
},
|
||||
],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
Entity {
|
||||
table_name: "cake_filling".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "cake_id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "filling_id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![
|
||||
Relation {
|
||||
ref_table: "cake".to_owned(),
|
||||
columns: vec!["cake_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::BelongsTo,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "filling".to_owned(),
|
||||
columns: vec!["filling_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::BelongsTo,
|
||||
},
|
||||
],
|
||||
primary_keys: vec![
|
||||
PrimaryKey {
|
||||
name: "cake_id".to_owned(),
|
||||
},
|
||||
PrimaryKey {
|
||||
name: "filling_id".to_owned(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Entity {
|
||||
table_name: "filling".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: true,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "name".to_owned(),
|
||||
col_type: ColumnType::String(Some(255)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![Relation {
|
||||
ref_table: "cake_filling".to_owned(),
|
||||
columns: vec![],
|
||||
ref_columns: vec![],
|
||||
rel_type: RelationType::HasMany,
|
||||
}],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
Entity {
|
||||
table_name: "fruit".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: true,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "name".to_owned(),
|
||||
col_type: ColumnType::String(Some(255)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "cake_id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![
|
||||
Relation {
|
||||
ref_table: "cake".to_owned(),
|
||||
columns: vec!["cake_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::BelongsTo,
|
||||
},
|
||||
Relation {
|
||||
ref_table: "vendor".to_owned(),
|
||||
columns: vec![],
|
||||
ref_columns: vec![],
|
||||
rel_type: RelationType::HasMany,
|
||||
},
|
||||
],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
Entity {
|
||||
table_name: "vendor".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: true,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "name".to_owned(),
|
||||
col_type: ColumnType::String(Some(255)),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "fruit_id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![Relation {
|
||||
ref_table: "fruit".to_owned(),
|
||||
columns: vec!["fruit_id".to_owned()],
|
||||
ref_columns: vec!["id".to_owned()],
|
||||
rel_type: RelationType::BelongsTo,
|
||||
}],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_code_blocks() -> io::Result<()> {
|
||||
let entities = setup();
|
||||
|
||||
assert_eq!(entities.len(), ENTITY_FILES.len());
|
||||
|
||||
for (i, entity) in entities.iter().enumerate() {
|
||||
let mut reader = BufReader::new(ENTITY_FILES[i].as_bytes());
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
|
||||
reader.read_until(b';', &mut Vec::new())?;
|
||||
|
||||
let mut line = String::new();
|
||||
while reader.read_line(&mut line)? > 0 {
|
||||
lines.push(line.to_owned());
|
||||
line.clear();
|
||||
}
|
||||
let content = lines.join("");
|
||||
let expected: TokenStream = content.parse().unwrap();
|
||||
let generated = EntityWriter::gen_code_blocks(entity)
|
||||
.into_iter()
|
||||
.skip(1)
|
||||
.fold(TokenStream::new(), |mut acc, tok| {
|
||||
acc.extend(tok);
|
||||
acc
|
||||
});
|
||||
assert_eq!(expected.to_string(), generated.to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
74
sea-orm-codegen/tests/entity/cake.rs
Normal file
74
sea-orm-codegen/tests/entity/cake.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Name,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
CakeFilling,
|
||||
Fruit,
|
||||
}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
match self {
|
||||
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::cake_filling::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::CakeFilling.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::fruit::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Fruit.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
81
sea-orm-codegen/tests/entity/cake_filling.rs
Normal file
81
sea-orm-codegen/tests/entity/cake_filling.rs
Normal file
@ -0,0 +1,81 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn table_name(&self) -> &str {
|
||||
"cake_filling"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||
pub struct Model {
|
||||
pub cake_id: i32,
|
||||
pub filling_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
CakeId,
|
||||
FillingId,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||
pub enum PrimaryKey {
|
||||
CakeId,
|
||||
FillingId,
|
||||
}
|
||||
|
||||
impl PrimaryKeyTrait for PrimaryKey {
|
||||
fn auto_increment() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
pub enum Relation {
|
||||
Cake,
|
||||
Filling,
|
||||
}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::CakeId => ColumnType::Integer.def(),
|
||||
Self::FillingId => 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::Filling => Entity::belongs_to(super::filling::Entity)
|
||||
.from(Column::FillingId)
|
||||
.to(super::filling::Column::Id)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::cake::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Cake.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::filling::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Filling.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
66
sea-orm-codegen/tests/entity/filling.rs
Normal file
66
sea-orm-codegen/tests/entity/filling.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn table_name(&self) -> &str {
|
||||
"filling"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||
pub struct Model {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Name,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
CakeFilling,
|
||||
}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
match self {
|
||||
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::cake_filling::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::CakeFilling.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
80
sea-orm-codegen/tests/entity/fruit.rs
Normal file
80
sea-orm-codegen/tests/entity/fruit.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn table_name(&self) -> &str {
|
||||
"fruit"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||
pub struct Model {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub cake_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Name,
|
||||
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 {
|
||||
Cake,
|
||||
Vendor,
|
||||
}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||
Self::CakeId => ColumnType::Integer.def().null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::Vendor => Entity::has_many(super::vendor::Entity).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::cake::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Cake.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::vendor::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Vendor.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
7
sea-orm-codegen/tests/entity/mod.rs
Normal file
7
sea-orm-codegen/tests/entity/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
pub mod cake;
|
||||
pub mod cake_filling;
|
||||
pub mod filling;
|
||||
pub mod fruit;
|
||||
pub mod vendor;
|
7
sea-orm-codegen/tests/entity/prelude.rs
Normal file
7
sea-orm-codegen/tests/entity/prelude.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
pub use super::cake::Entity as Cake;
|
||||
pub use super::cake_filling::Entity as CakeFilling;
|
||||
pub use super::filling::Entity as Filling;
|
||||
pub use super::fruit::Entity as Fruit;
|
||||
pub use super::vendor::Entity as Vendor;
|
72
sea-orm-codegen/tests/entity/vendor.rs
Normal file
72
sea-orm-codegen/tests/entity/vendor.rs
Normal file
@ -0,0 +1,72 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn table_name(&self) -> &str {
|
||||
"vendor"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||
pub struct Model {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub fruit_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Name,
|
||||
FruitId,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
Fruit,
|
||||
}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||
Self::FruitId => ColumnType::Integer.def().null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
match self {
|
||||
Self::Fruit => Entity::belongs_to(super::fruit::Entity)
|
||||
.from(Column::FruitId)
|
||||
.to(super::fruit::Column::Id)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::fruit::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Fruit.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
127
sea-orm-codegen/tests/mod.rs
Normal file
127
sea-orm-codegen/tests/mod.rs
Normal file
@ -0,0 +1,127 @@
|
||||
mod entity;
|
||||
|
||||
use entity::*;
|
||||
|
||||
use sea_orm::{entity::*, error::*, MockDatabase, MockExecResult, Transaction};
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_insert() -> Result<(), DbErr> {
|
||||
let exec_result = MockExecResult {
|
||||
last_insert_id: 1,
|
||||
rows_affected: 1,
|
||||
};
|
||||
|
||||
let db = MockDatabase::new()
|
||||
.append_exec_results(vec![exec_result.clone()])
|
||||
.into_connection();
|
||||
|
||||
let apple = cake::ActiveModel {
|
||||
name: Set("Apple Pie".to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||
|
||||
assert_eq!(insert_result.last_insert_id, exec_result.last_insert_id);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::from_sql_and_values(
|
||||
r#"INSERT INTO "cake" ("name") VALUES ($1)"#,
|
||||
vec!["Apple Pie".into()]
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_select() -> Result<(), DbErr> {
|
||||
let query_results = vec![cake_filling::Model {
|
||||
cake_id: 2,
|
||||
filling_id: 3,
|
||||
}];
|
||||
|
||||
let db = MockDatabase::new()
|
||||
.append_query_results(vec![query_results.clone()])
|
||||
.into_connection();
|
||||
|
||||
let selected_models = cake_filling::Entity::find_by_id((2, 3)).all(&db).await?;
|
||||
|
||||
assert_eq!(selected_models, query_results);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::from_sql_and_values([
|
||||
r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
|
||||
r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
|
||||
].join(" ").as_str(),
|
||||
vec![2i32.into(), 3i32.into()]
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_update() -> Result<(), DbErr> {
|
||||
let exec_result = MockExecResult {
|
||||
last_insert_id: 1,
|
||||
rows_affected: 1,
|
||||
};
|
||||
|
||||
let db = MockDatabase::new()
|
||||
.append_exec_results(vec![exec_result.clone()])
|
||||
.into_connection();
|
||||
|
||||
let orange = fruit::ActiveModel {
|
||||
id: Set(1),
|
||||
name: Set("Orange".to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let updated_model = fruit::Entity::update(orange.clone()).exec(&db).await?;
|
||||
|
||||
assert_eq!(updated_model, orange);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::from_sql_and_values(
|
||||
r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2"#,
|
||||
vec!["Orange".into(), 1i32.into()]
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_delete() -> Result<(), DbErr> {
|
||||
let exec_result = MockExecResult {
|
||||
last_insert_id: 1,
|
||||
rows_affected: 1,
|
||||
};
|
||||
|
||||
let db = MockDatabase::new()
|
||||
.append_exec_results(vec![exec_result.clone()])
|
||||
.into_connection();
|
||||
|
||||
let orange = fruit::ActiveModel {
|
||||
id: Set(3),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
|
||||
|
||||
assert_eq!(delete_result.rows_affected, exec_result.rows_affected);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::from_sql_and_values(
|
||||
r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
|
||||
vec![3i32.into()]
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -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> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{error::*, ExecResult, QueryResult, Statement};
|
||||
use crate::{error::*, ExecResult, QueryResult, Statement, Syntax};
|
||||
use sea_query::{
|
||||
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder,
|
||||
SqliteQueryBuilder,
|
||||
@ -134,29 +134,49 @@ impl DatabaseConnection {
|
||||
}
|
||||
|
||||
impl QueryBuilderBackend {
|
||||
pub fn syntax(&self) -> Syntax {
|
||||
match self {
|
||||
Self::MySql => Syntax::MySql,
|
||||
Self::Postgres => Syntax::Postgres,
|
||||
Self::Sqlite => Syntax::Sqlite,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build<S>(&self, statement: &S) -> Statement
|
||||
where
|
||||
S: QueryStatementBuilder,
|
||||
{
|
||||
Statement::from_string_values_tuple(
|
||||
self.syntax(),
|
||||
match self {
|
||||
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemaBuilderBackend {
|
||||
pub fn syntax(&self) -> Syntax {
|
||||
match self {
|
||||
Self::MySql => Syntax::MySql,
|
||||
Self::Postgres => Syntax::Postgres,
|
||||
Self::Sqlite => Syntax::Sqlite,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build<S>(&self, statement: &S) -> Statement
|
||||
where
|
||||
S: SchemaStatementBuilder,
|
||||
{
|
||||
Statement::from_string(
|
||||
self.syntax(),
|
||||
match self {
|
||||
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +79,7 @@ impl MockDatabaseTrait for MockDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
fn query(
|
||||
&mut self,
|
||||
counter: usize,
|
||||
statement: Statement,
|
||||
) -> Result<Vec<QueryResult>, DbErr> {
|
||||
fn query(&mut self, counter: usize, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||
self.transaction_log.push(Transaction::one(statement));
|
||||
if counter < self.query_results.len() {
|
||||
Ok(std::mem::take(&mut self.query_results[counter])
|
||||
|
@ -29,6 +29,9 @@ impl Database {
|
||||
if crate::MockDatabaseConnector::accepts(string) {
|
||||
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
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,38 @@
|
||||
use sea_query::{inject_parameters, MySqlQueryBuilder, Values};
|
||||
use crate::QueryBuilderWithSyntax;
|
||||
use sea_query::{
|
||||
inject_parameters, MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder,
|
||||
Values,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum Syntax {
|
||||
MySql,
|
||||
Postgres,
|
||||
Sqlite,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Statement {
|
||||
pub sql: String,
|
||||
pub values: Option<Values>,
|
||||
pub syntax: Syntax,
|
||||
}
|
||||
|
||||
impl From<String> for Statement {
|
||||
fn from(stmt: String) -> Statement {
|
||||
impl Statement {
|
||||
pub fn from_string(syntax: Syntax, stmt: String) -> Statement {
|
||||
Statement {
|
||||
sql: stmt,
|
||||
values: None,
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(String, Values)> for Statement {
|
||||
fn from(stmt: (String, Values)) -> Statement {
|
||||
pub fn from_string_values_tuple(syntax: Syntax, stmt: (String, Values)) -> Statement {
|
||||
Statement {
|
||||
sql: stmt.0,
|
||||
values: Some(stmt.1),
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,8 +41,11 @@ impl fmt::Display for Statement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.values {
|
||||
Some(values) => {
|
||||
let string =
|
||||
inject_parameters(&self.sql, values.0.clone(), &MySqlQueryBuilder::default());
|
||||
let string = inject_parameters(
|
||||
&self.sql,
|
||||
values.0.clone(),
|
||||
self.syntax.get_query_builder().as_ref(),
|
||||
);
|
||||
write!(f, "{}", &string)
|
||||
}
|
||||
None => {
|
||||
@ -39,3 +54,31 @@ impl fmt::Display for Statement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Syntax {
|
||||
pub fn get_query_builder(&self) -> Box<dyn QueryBuilder> {
|
||||
match self {
|
||||
Self::MySql => Box::new(MysqlQueryBuilder),
|
||||
Self::Postgres => Box::new(PostgresQueryBuilder),
|
||||
Self::Sqlite => Box::new(SqliteQueryBuilder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryBuilderWithSyntax for MysqlQueryBuilder {
|
||||
fn syntax(&self) -> Syntax {
|
||||
Syntax::MySql
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryBuilderWithSyntax for PostgresQueryBuilder {
|
||||
fn syntax(&self) -> Syntax {
|
||||
Syntax::Postgres
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryBuilderWithSyntax for SqliteQueryBuilder {
|
||||
fn syntax(&self) -> Syntax {
|
||||
Syntax::Sqlite
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::Statement;
|
||||
use crate::{Statement, Syntax};
|
||||
use sea_query::{Value, Values};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -11,10 +11,10 @@ impl Transaction {
|
||||
where
|
||||
I: IntoIterator<Item = Value>,
|
||||
{
|
||||
Self::one(Statement {
|
||||
sql: sql.to_owned(),
|
||||
values: Some(Values(values.into_iter().collect())),
|
||||
})
|
||||
Self::one(Statement::from_string_values_tuple(
|
||||
Syntax::Postgres,
|
||||
(sql.to_string(), Values(values.into_iter().collect())),
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a Transaction with one statement
|
||||
|
@ -49,7 +49,9 @@ impl SqlxMySqlPoolConnection {
|
||||
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
||||
}
|
||||
} 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 {
|
||||
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)),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
||||
Err(DbErr::Query(
|
||||
"Failed to acquire connection from pool.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,9 @@ impl SqlxSqlitePoolConnection {
|
||||
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
||||
}
|
||||
} 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 {
|
||||
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)),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
||||
Err(DbErr::Query(
|
||||
"Failed to acquire connection from pool.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +227,11 @@ where
|
||||
let model: Option<E::Model> = found?;
|
||||
match 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 {
|
||||
Ok(A::default())
|
||||
@ -243,6 +247,7 @@ where
|
||||
exec.await
|
||||
}
|
||||
|
||||
/// Delete an active model by its primary key
|
||||
pub async fn delete_active_model<A, E>(
|
||||
mut am: A,
|
||||
db: &DatabaseConnection,
|
||||
|
@ -18,7 +18,18 @@ pub trait EntityName: IdenStatic + Default {
|
||||
Self::table_name(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Each table in database correspond to a Entity implemented [`EntityTrait`].
|
||||
///
|
||||
/// This trait provides an API for you to inspect it's properties
|
||||
/// - Column (implemented [`ColumnTrait`])
|
||||
/// - Relation (implemented [`RelationTrait`])
|
||||
/// - Primary Key (implemented [`PrimaryKeyTrait`] and [`PrimaryKeyToColumn`])
|
||||
///
|
||||
/// This trait also provides an API for CRUD actions
|
||||
/// - Select: `find`, `find_*`
|
||||
/// - Insert: `insert`, `insert_*`
|
||||
/// - Update: `update`, `update_*`
|
||||
/// - Delete: `delete`, `delete_*`
|
||||
pub trait EntityTrait: EntityName {
|
||||
type Model: ModelTrait<Entity = Self> + FromQueryResult;
|
||||
|
||||
@ -49,16 +60,66 @@ pub trait EntityTrait: EntityName {
|
||||
RelationBuilder::from_rel(RelationType::HasMany, R::to().rev())
|
||||
}
|
||||
|
||||
/// Construct select statement to find one / all models
|
||||
///
|
||||
/// - To select columns, join tables and group by expressions, see [`QuerySelect`](crate::query::QuerySelect)
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
/// - To apply order by expressions, see [`QueryOrder`](crate::query::QueryOrder)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![
|
||||
/// # cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "New York Cheese".to_owned(),
|
||||
/// # },
|
||||
/// # ],
|
||||
/// # vec![
|
||||
/// # cake::Model {
|
||||
/// # id: 1,
|
||||
/// # name: "New York Cheese".to_owned(),
|
||||
/// # },
|
||||
/// # cake::Model {
|
||||
/// # id: 2,
|
||||
/// # name: "Chocolate Forest".to_owned(),
|
||||
/// # },
|
||||
/// # ],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// cake::Entity::find().one(&db).await;
|
||||
/// cake::Entity::find().all(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find().one(&db).await?,
|
||||
/// Some(cake::Model {
|
||||
/// id: 1,
|
||||
/// name: "New York Cheese".to_owned(),
|
||||
/// })
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find().all(&db).await?,
|
||||
/// vec![
|
||||
/// cake::Model {
|
||||
/// id: 1,
|
||||
/// name: "New York Cheese".to_owned(),
|
||||
/// },
|
||||
/// cake::Model {
|
||||
/// id: 2,
|
||||
/// name: "Chocolate Forest".to_owned(),
|
||||
/// },
|
||||
/// ]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -77,15 +138,37 @@ pub trait EntityTrait: EntityName {
|
||||
}
|
||||
|
||||
/// Find a model by primary key
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![
|
||||
/// # cake::Model {
|
||||
/// # id: 11,
|
||||
/// # name: "Sponge Cake".to_owned(),
|
||||
/// # },
|
||||
/// # ],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// cake::Entity::find_by_id(11).all(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find_by_id(11).all(&db).await?,
|
||||
/// vec![cake::Model {
|
||||
/// id: 11,
|
||||
/// name: "Sponge Cake".to_owned(),
|
||||
/// }]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -97,13 +180,32 @@ pub trait EntityTrait: EntityName {
|
||||
/// Find by composite key
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_query_results(vec![
|
||||
/// # vec![
|
||||
/// # cake_filling::Model {
|
||||
/// # cake_id: 2,
|
||||
/// # filling_id: 3,
|
||||
/// # },
|
||||
/// # ],
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await?,
|
||||
/// vec![cake_filling::Model {
|
||||
/// cake_id: 2,
|
||||
/// filling_id: 3,
|
||||
/// }]
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -135,10 +237,22 @@ pub trait EntityTrait: EntityName {
|
||||
select
|
||||
}
|
||||
|
||||
/// Insert an model into database
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 15,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
@ -147,8 +261,14 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// cake::Entity::insert(apple).exec(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(insert_result.last_insert_id, 15);
|
||||
/// // assert_eq!(insert_result.rows_affected, 1);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -164,10 +284,22 @@ pub trait EntityTrait: EntityName {
|
||||
Insert::one(model)
|
||||
}
|
||||
|
||||
/// Insert many models into database
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 28,
|
||||
/// # rows_affected: 2,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||
///
|
||||
@ -180,8 +312,14 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// cake::Entity::insert_many(vec![apple, orange]).exec(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// let insert_result = cake::Entity::insert_many(vec![apple, orange]).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(insert_result.last_insert_id, 28);
|
||||
/// // assert_eq!(insert_result.rows_affected, 2);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -199,10 +337,24 @@ pub trait EntityTrait: EntityName {
|
||||
Insert::many(models)
|
||||
}
|
||||
|
||||
/// Update an model in database
|
||||
///
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
@ -212,8 +364,14 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// fruit::Entity::update(orange).exec(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// assert_eq!(
|
||||
/// fruit::Entity::update(orange.clone()).exec(&db).await?, // Clone here because we need to assert_eq
|
||||
/// orange
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -229,19 +387,38 @@ pub trait EntityTrait: EntityName {
|
||||
Update::one(model)
|
||||
}
|
||||
|
||||
/// Update many models in database
|
||||
///
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 5,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::{Expr, Value}};
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// fruit::Entity::update_many()
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// let update_result = fruit::Entity::update_many()
|
||||
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
|
||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||
/// .exec(&db)
|
||||
/// .await;
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(update_result.rows_affected, 5);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -254,10 +431,24 @@ pub trait EntityTrait: EntityName {
|
||||
Update::many(Self::default())
|
||||
}
|
||||
|
||||
/// Delete an model from database
|
||||
///
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 1,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
@ -266,8 +457,13 @@ pub trait EntityTrait: EntityName {
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// fruit::Entity::delete(orange).exec(&db).await;
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
|
||||
///
|
||||
/// assert_eq!(delete_result.rows_affected, 1);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
@ -283,18 +479,37 @@ pub trait EntityTrait: EntityName {
|
||||
Delete::one(model)
|
||||
}
|
||||
|
||||
/// Delete many models from database
|
||||
///
|
||||
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "mock")]
|
||||
/// # use sea_orm::{MockDatabase, Transaction};
|
||||
/// # let db = MockDatabase::new().into_connection();
|
||||
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||
/// #
|
||||
/// # let db = MockDatabase::new()
|
||||
/// # .append_exec_results(vec![
|
||||
/// # MockExecResult {
|
||||
/// # last_insert_id: 0,
|
||||
/// # rows_affected: 5,
|
||||
/// # },
|
||||
/// # ])
|
||||
/// # .into_connection();
|
||||
/// #
|
||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||
///
|
||||
/// # async_std::task::block_on(async {
|
||||
/// fruit::Entity::delete_many()
|
||||
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||
/// #
|
||||
/// let delete_result = fruit::Entity::delete_many()
|
||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||
/// .exec(&db)
|
||||
/// .await;
|
||||
/// .await?;
|
||||
///
|
||||
/// assert_eq!(delete_result.rows_affected, 5);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # });
|
||||
///
|
||||
/// assert_eq!(
|
||||
|
@ -66,6 +66,7 @@ macro_rules! bind_vec_func {
|
||||
}
|
||||
|
||||
// LINT: when the operand value does not match column type
|
||||
/// Wrapper of the identically named method in [`sea_query::Expr`]
|
||||
pub trait ColumnTrait: IdenStatic + Iterable {
|
||||
type EntityName: EntityName;
|
||||
|
||||
|
@ -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;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -62,10 +62,7 @@ async fn exec_delete_only(
|
||||
}
|
||||
|
||||
// Only Statement impl Send
|
||||
async fn exec_delete(
|
||||
statement: Statement,
|
||||
db: &DatabaseConnection,
|
||||
) -> Result<DeleteResult, DbErr> {
|
||||
async fn exec_delete(statement: Statement, db: &DatabaseConnection) -> Result<DeleteResult, DbErr> {
|
||||
let result = db.execute(statement).await?;
|
||||
Ok(DeleteResult {
|
||||
rows_affected: result.rows_affected(),
|
||||
|
@ -40,10 +40,7 @@ impl Inserter {
|
||||
}
|
||||
|
||||
// Only Statement impl Send
|
||||
async fn exec_insert(
|
||||
statement: Statement,
|
||||
db: &DatabaseConnection,
|
||||
) -> Result<InsertResult, DbErr> {
|
||||
async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result<InsertResult, DbErr> {
|
||||
let result = db.execute(statement).await?;
|
||||
// TODO: Postgres instead use query_one + returning clause
|
||||
Ok(InsertResult {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::DbErr;
|
||||
use chrono::NaiveDateTime;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -56,12 +57,14 @@ macro_rules! try_getable_all {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(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")]
|
||||
QueryResultRow::SqlxSqlite(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")]
|
||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||
@ -109,7 +112,8 @@ macro_rules! try_getable_mysql {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(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")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
@ -160,6 +164,7 @@ try_getable_mysql!(u64);
|
||||
try_getable_all!(f32);
|
||||
try_getable_all!(f64);
|
||||
try_getable_all!(String);
|
||||
try_getable_all!(NaiveDateTime);
|
||||
|
||||
#[cfg(feature = "with-rust_decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
@ -172,14 +177,18 @@ impl TryGetable for Decimal {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(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")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
let val: f64 = row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)?;
|
||||
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()))
|
||||
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())?),
|
||||
|
@ -18,10 +18,7 @@ impl<'a, A: 'a> UpdateOne<A>
|
||||
where
|
||||
A: ActiveModelTrait,
|
||||
{
|
||||
pub fn exec(
|
||||
self,
|
||||
db: &'a DatabaseConnection,
|
||||
) -> impl Future<Output = Result<A, DbErr>> + 'a {
|
||||
pub fn exec(self, db: &'a DatabaseConnection) -> impl Future<Output = Result<A, DbErr>> + 'a {
|
||||
// so that self is dropped before entering await
|
||||
exec_update_and_return_original(self.query, self.model, db)
|
||||
}
|
||||
@ -74,10 +71,7 @@ where
|
||||
}
|
||||
|
||||
// Only Statement impl Send
|
||||
async fn exec_update(
|
||||
statement: Statement,
|
||||
db: &DatabaseConnection,
|
||||
) -> Result<UpdateResult, DbErr> {
|
||||
async fn exec_update(statement: Statement, db: &DatabaseConnection) -> Result<UpdateResult, DbErr> {
|
||||
let result = db.execute(statement).await?;
|
||||
Ok(UpdateResult {
|
||||
rows_affected: result.rows_affected(),
|
||||
|
@ -177,9 +177,9 @@
|
||||
//! Licensed under either of
|
||||
//!
|
||||
//! - Apache License, Version 2.0
|
||||
//! ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//! ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
//! - MIT license
|
||||
//! ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
//! ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
//!
|
||||
//! at your option.
|
||||
//!
|
||||
@ -215,5 +215,5 @@ pub use sea_orm_macros::{
|
||||
};
|
||||
pub use sea_query;
|
||||
pub use sea_query::Iden;
|
||||
pub use strum::EnumIter;
|
||||
pub use strum;
|
||||
pub use strum::EnumIter;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{FromQueryResult, DbErr, QueryResult, QueryResultRow};
|
||||
use crate::{DbErr, FromQueryResult, QueryResult, QueryResultRow};
|
||||
use serde_json::Map;
|
||||
pub use serde_json::Value as JsonValue;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::Statement;
|
||||
use crate::{Statement, Syntax};
|
||||
use sea_query::{QueryBuilder, QueryStatementBuilder};
|
||||
|
||||
pub trait QueryTrait {
|
||||
@ -16,8 +16,12 @@ pub trait QueryTrait {
|
||||
/// Build the query as [`Statement`]
|
||||
fn build<B>(&self, builder: B) -> Statement
|
||||
where
|
||||
B: QueryBuilder,
|
||||
B: QueryBuilderWithSyntax,
|
||||
{
|
||||
self.as_query().build(builder).into()
|
||||
Statement::from_string_values_tuple(builder.syntax(), self.as_query().build(builder))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QueryBuilderWithSyntax: QueryBuilder {
|
||||
fn syntax(&self) -> Syntax;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use chrono::NaiveDateTime;
|
||||
use rust_decimal::prelude::*;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
@ -16,7 +17,7 @@ pub struct Model {
|
||||
pub total: Decimal,
|
||||
pub bakery_id: Option<i32>,
|
||||
pub customer_id: Option<i32>,
|
||||
pub placed_at: String,
|
||||
pub placed_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbConn};
|
||||
use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbConn, Statement, Syntax};
|
||||
|
||||
mod setup;
|
||||
|
||||
@ -27,7 +27,9 @@ async fn setup_schema(db: &DbConn) {
|
||||
.col(ColumnDef::new(cake::Column::Name).string())
|
||||
.build(SqliteQueryBuilder);
|
||||
|
||||
let result = db.execute(stmt.into()).await;
|
||||
let result = db
|
||||
.execute(Statement::from_string(Syntax::Sqlite, stmt))
|
||||
.await;
|
||||
println!("Create table cake: {:?}", result);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub use super::*;
|
||||
use chrono::offset::Utc;
|
||||
use rust_decimal_macros::dec;
|
||||
|
||||
pub async fn test_create_lineitem(db: &DbConn) {
|
||||
@ -64,7 +65,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
||||
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("placeholder".to_string()),
|
||||
placed_at: Set(Utc::now().naive_utc()),
|
||||
..Default::default()
|
||||
};
|
||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub use super::*;
|
||||
use chrono::offset::Utc;
|
||||
use rust_decimal_macros::dec;
|
||||
|
||||
pub async fn test_create_order(db: &DbConn) {
|
||||
@ -65,7 +66,7 @@ pub async fn test_create_order(db: &DbConn) {
|
||||
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("placeholder".to_string()),
|
||||
placed_at: Set(Utc::now().naive_utc()),
|
||||
..Default::default()
|
||||
};
|
||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user