#![allow(unused_imports, dead_code)]
use entity::{Column, Entity};
use sea_orm::{
DerivePartialModel, IntoActiveModel, JoinType, NotSet, QueryOrder, QuerySelect, Set,
prelude::*, sea_query::Alias,
};
use sea_query::ExprTrait;
use crate::common::TestContext;
use common::bakery_chain::*;
mod common;
mod entity {
use sea_orm::prelude::*;
#[derive(Debug, Clone, DeriveEntityModel)]
#[sea_orm(table_name = "foo_table")]
pub struct Model {
#[sea_orm(primary_key)]
id: i32,
foo: i32,
bar: String,
foo2: bool,
bar2: f64,
}
#[derive(Debug, DeriveRelation, EnumIter)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "Entity")]
struct SimpleTest {
foo: i32,
bar: String,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "<entity::Model as ModelTrait>::Entity")]
struct EntityNameNotAIdent {
#[sea_orm(from_col = "foo2")]
foo: i32,
#[sea_orm(from_col = "bar2")]
bar: String,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "Entity")]
struct FieldFromDiffNameColumnTest {
#[sea_orm(from_col = "foo2")]
foo: i32,
#[sea_orm(from_col = "bar2")]
bar: String,
}
#[derive(DerivePartialModel)]
struct FieldFromExpr {
#[sea_orm(from_expr = "Column::Bar2.sum()")]
foo: f64,
#[sea_orm(from_expr = "Expr::col(Column::Id).equals(Column::Foo)")]
bar: bool,
}
#[derive(DerivePartialModel)]
struct Nest {
#[sea_orm(nested)]
foo: SimpleTest,
}
#[derive(DerivePartialModel)]
struct NestOption {
#[sea_orm(nested)]
foo: Option<SimpleTest>,
}
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity")]
struct Bakery {
id: i32,
#[sea_orm(from_col = "Name")]
title: String,
}
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct Cake {
id: i32,
name: String,
#[sea_orm(nested)]
bakery: Option<Bakery>,
#[sea_orm(skip)]
ignore: Ignore,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity")]
struct BakeryDetails {
#[sea_orm(nested)]
basics: Bakery,
#[sea_orm(from_expr = "bakery::Column::ProfitMargin")]
profit: f64,
}
#[derive(Debug, Default, PartialEq, Eq)]
struct Ignore {}
#[sea_orm_macros::test]
fn partial_model_left_join_does_not_exist() {
let ctx = TestContext::new("partial_model_left_join_does_not_exist");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, false);
let cake: Cake = cake::Entity::find()
.left_join(bakery::Entity)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert!(cake.bakery.is_none());
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_left_join_with_optional_nested_model_optional_fields_does_not_exist() {
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "baker::Entity")]
struct BakerDetails {
id: i32,
name: String,
bakery_id: Option<i32>,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "baker::Entity")]
struct NestedBaker {
#[sea_orm(nested)]
details: BakerDetails,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "cake::Entity")]
struct CakeWithOptionalBakerModel {
id: i32,
name: String,
#[sea_orm(nested)]
baker: Option<NestedBaker>,
}
let ctx = TestContext::new("partial_model_left_join_deep_baker");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let cakes: Vec<CakeWithOptionalBakerModel> = cake::Entity::find()
.left_join(baker::Entity)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.all(&ctx.db)
.expect("succeeds to get the result");
assert_eq!(
cakes,
[
CakeWithOptionalBakerModel {
id: 13,
name: "Cheesecake".to_owned(),
baker: Some(NestedBaker {
details: BakerDetails {
id: 22,
name: "Master Baker".to_owned(),
bakery_id: Some(42),
},
}),
},
CakeWithOptionalBakerModel {
id: 15,
name: "Chocolate".to_owned(),
baker: None,
},
]
);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_left_join_exists() {
let ctx = TestContext::new("partial_model_left_join_exists");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let cake: Cake = cake::Entity::find()
.left_join(bakery::Entity)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert!(matches!(cake.bakery, Some(Bakery { id: 42, .. })));
assert_eq!(cake.bakery.as_ref().unwrap().title, "cool little bakery");
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct Cake2 {
id: i32,
name: String,
#[sea_orm(nested, alias = "r0")]
bakery: Option<Bakery>,
#[sea_orm(skip)]
ignore: Ignore,
}
let cake2: Cake2 = cake::Entity::find()
.left_join_linked(cake::ToBakery)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(
format!("{cake:?}").trim_start_matches("Cake "),
format!("{cake2:?}").trim_start_matches("Cake2 ")
);
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct Cake3 {
id: i32,
name: String,
#[sea_orm(nested)]
bakery: Option<bakery::Model>,
}
let cake: Cake3 = cake::Entity::find()
.left_join(bakery::Entity)
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert_eq!(
cake.bakery.unwrap(),
bakery::Model {
id: 42,
name: "cool little bakery".to_string(),
profit_margin: 4.1,
}
);
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct Cake4 {
id: i32,
price: Decimal,
#[sea_orm(from_expr = "cake::COLUMN.price.add(\"0.2\".parse::<Decimal>().unwrap())")]
price_vat: Decimal,
}
let cake: Cake4 = cake::Entity::find()
.order_by_asc(cake::COLUMN.id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.price, "2".parse().unwrap());
assert_eq!(cake.price_vat, "2.2".parse().unwrap());
ctx.delete();
}
#[sea_orm_macros::test]
fn model_as_partial_model() {
let ctx = TestContext::new("model_as_partial_model");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, false);
let bakery: bakery::Model = bakery::Entity::find()
.order_by_asc(bakery::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(
bakery,
bakery::Model {
id: 42,
name: "cool little bakery".to_string(),
profit_margin: 4.1,
}
);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_left_join_alias() {
#[derive(DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity")]
struct Factory {
id: i32,
#[sea_orm(from_col = "name")]
plant: String,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct CakeFactory {
id: i32,
name: String,
#[sea_orm(nested, alias = "factory")]
bakery: Option<Factory>,
}
let ctx = TestContext::new("partial_model_left_join_alias");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let cake: CakeFactory = cake::Entity::find()
.join_as(JoinType::LeftJoin, cake::Relation::Bakery.def(), "factory")
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert!(matches!(cake.bakery, Some(Factory { id: 42, .. })));
assert_eq!(cake.bakery.unwrap().plant, "cool little bakery");
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_left_join_alias_old() {
#[derive(DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity", alias = "factory")]
struct Factory {
id: i32,
#[sea_orm(from_col = "name")]
plant: String,
}
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct CakeFactory {
id: i32,
name: String,
#[sea_orm(nested)]
bakery: Option<Factory>,
}
let ctx = TestContext::new("partial_model_left_join_alias_old");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let cake: CakeFactory = cake::Entity::find()
.join_as(JoinType::LeftJoin, cake::Relation::Bakery.def(), "factory")
.order_by_asc(cake::Column::Id)
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(cake.id, 13);
assert_eq!(cake.name, "Cheesecake");
assert!(matches!(cake.bakery, Some(Factory { id: 42, .. })));
assert_eq!(cake.bakery.unwrap().plant, "cool little bakery");
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_flat() {
let ctx = TestContext::new("partial_model_flat");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let bakery: Bakery = bakery::Entity::find()
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(bakery.id, 42);
assert_eq!(bakery.title, "cool little bakery");
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_nested() {
let ctx = TestContext::new("partial_model_nested");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
let bakery: BakeryDetails = bakery::Entity::find()
.into_partial_model()
.one(&ctx.db)
.expect("succeeds to get the result")
.expect("exactly one model in DB");
assert_eq!(bakery.basics.id, 42);
assert_eq!(bakery.basics.title, "cool little bakery");
assert_eq!(bakery.profit, 4.1);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_join_three() {
let ctx = TestContext::new("partial_model_join_three");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "order::Entity")]
struct OrderItem {
id: i32,
total: Decimal,
#[sea_orm(nested)]
customer: Customer,
#[sea_orm(nested)]
line: LineItem,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "customer::Entity")]
struct Customer {
name: String,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "lineitem::Entity")]
struct LineItem {
price: Decimal,
quantity: i32,
#[sea_orm(nested)]
cake: Cake,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "cake::Entity")]
struct Cake {
name: String,
}
let items: Vec<OrderItem> = order::Entity::find()
.left_join(customer::Entity)
.left_join(lineitem::Entity)
.join(JoinType::LeftJoin, lineitem::Relation::Cake.def())
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.into_partial_model()
.all(&ctx.db)
.unwrap();
assert_eq!(
items,
[
OrderItem {
id: 101,
total: Decimal::from(10),
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
cake: Cake {
name: "Cheesecake".to_owned()
},
price: Decimal::from(2),
quantity: 2,
}
},
OrderItem {
id: 101,
total: Decimal::from(10),
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
cake: Cake {
name: "Chocolate".to_owned()
},
price: Decimal::from(3),
quantity: 2,
}
}
]
);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_join_three_flat() {
let ctx = TestContext::new("partial_model_join_three_flat");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "order::Entity")]
struct OrderItem {
#[sea_orm(nested)]
order: Order,
#[sea_orm(nested)]
customer: Customer,
#[sea_orm(nested)]
line: LineItem,
#[sea_orm(nested)]
cake: Cake,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "order::Entity")]
struct Order {
#[sea_orm(from_col = "id")]
order_id: i32,
total: Decimal,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "customer::Entity")]
struct Customer {
name: String,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "lineitem::Entity")]
struct LineItem {
price: Decimal,
quantity: i32,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "cake::Entity")]
struct Cake {
name: String,
}
let items: Vec<OrderItem> = order::Entity::find()
.left_join(customer::Entity)
.left_join(lineitem::Entity)
.join(JoinType::LeftJoin, lineitem::Relation::Cake.def())
.order_by_asc(order::Column::Id)
.order_by_asc(lineitem::Column::Id)
.into_partial_model()
.all(&ctx.db)
.unwrap();
assert_eq!(
items,
[
OrderItem {
order: Order {
order_id: 101,
total: Decimal::from(10),
},
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
price: Decimal::from(2),
quantity: 2,
},
cake: Cake {
name: "Cheesecake".to_owned()
},
},
OrderItem {
order: Order {
order_id: 101,
total: Decimal::from(10),
},
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
price: Decimal::from(3),
quantity: 2,
},
cake: Cake {
name: "Chocolate".to_owned()
},
}
]
);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_join_two_linked() {
let ctx = TestContext::new("partial_model_join_two_linked");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, true);
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "order::Entity")]
struct OrderItem {
#[sea_orm(nested)]
order: Order,
#[sea_orm(nested, alias = "r0")]
customer: Customer,
#[sea_orm(nested, alias = "r1")]
line: LineItem,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "order::Entity")]
struct Order {
#[sea_orm(from_col = "id")]
order_id: i32,
total: Decimal,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "customer::Entity")]
struct Customer {
name: String,
}
#[derive(Debug, DerivePartialModel, PartialEq)]
#[sea_orm(entity = "lineitem::Entity")]
struct LineItem {
price: Decimal,
quantity: i32,
}
let items: Vec<OrderItem> = order::Entity::find()
.left_join_linked(order::ToCustomer)
.left_join_linked(order::ToLineitem)
.order_by_asc(order::Column::Id)
.order_by_asc(Expr::col(("r1", lineitem::Column::Id)))
.into_partial_model()
.all(&ctx.db)
.unwrap();
assert_eq!(
items,
[
OrderItem {
order: Order {
order_id: 101,
total: Decimal::from(10),
},
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
price: Decimal::from(2),
quantity: 2,
},
},
OrderItem {
order: Order {
order_id: 101,
total: Decimal::from(10),
},
customer: Customer {
name: "Bob".to_owned()
},
line: LineItem {
price: Decimal::from(3),
quantity: 2,
},
}
]
);
ctx.delete();
}
#[sea_orm_macros::test]
fn partial_model_into_active_model() {
let mut cake = Cake {
id: 12,
name: "Lemon Drizzle".to_owned(),
bakery: None,
ignore: Ignore {},
}
.into_active_model();
cake.serial = NotSet;
assert_eq!(
cake,
cake::ActiveModel {
id: Set(12),
name: Set("Lemon Drizzle".to_owned()),
serial: NotSet,
..Default::default()
}
);
assert_eq!(
cake::ActiveModel {
..cake.into_active_model()
},
cake::ActiveModel {
id: Set(12),
name: Set("Lemon Drizzle".to_owned()),
serial: NotSet,
..Default::default()
}
);
}
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "bakery::Entity")]
struct WrongBakery {
id: String,
#[sea_orm(from_col = "Name")]
title: String,
}
#[derive(Debug, DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct WrongCake {
id: i32,
name: String,
#[sea_orm(nested)]
bakery: Option<WrongBakery>,
}
#[sea_orm_macros::test]
#[ignore = "This currently does not work, as sqlx does not perform type checking when a column is absent.."]
fn partial_model_optional_field_but_type_error() {
let ctx = TestContext::new("partial_model_nested");
create_tables(&ctx.db).unwrap();
seed_data::init_1(&ctx, false);
let _: DbErr = cake::Entity::find()
.left_join(bakery::Entity)
.into_partial_model::<WrongCake>()
.one(&ctx.db)
.expect_err("should error instead of returning an empty Option");
ctx.delete();
}