diff --git a/sea-orm-macros/src/attributes.rs b/sea-orm-macros/src/attributes.rs index bd893d77..fbfa0778 100644 --- a/sea-orm-macros/src/attributes.rs +++ b/sea-orm-macros/src/attributes.rs @@ -21,6 +21,8 @@ pub mod field_attr { pub belongs_to: Option, pub has_one: Option, pub has_many: Option, + pub on_update: Option, + pub on_delete: Option, pub from: Option, pub to: Option, } diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index 6066b85c..fbaaef15 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -165,6 +165,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res "NaiveTime" => quote! { Time }, "DateTime" | "NaiveDateTime" => quote! { DateTime }, "Uuid" => quote! { Uuid }, + "Json" => quote! { Json }, "Decimal" => quote! { Decimal }, _ => { return Err(Error::new( diff --git a/sea-orm-macros/src/derives/relation.rs b/sea-orm-macros/src/derives/relation.rs index 290d02d0..a674a803 100644 --- a/sea-orm-macros/src/derives/relation.rs +++ b/sea-orm-macros/src/derives/relation.rs @@ -114,6 +114,28 @@ impl DeriveRelation { return Err(syn::Error::new_spanned(variant, "Missing attribute 'to'")); } + if attr.on_update.is_some() { + let on_update = attr + .on_update + .as_ref() + .map(Self::parse_lit_string) + .ok_or_else(|| { + syn::Error::new_spanned(variant, "Missing value for 'on_update'") + })??; + result = quote! { #result.on_update(sea_orm::prelude::ForeignKeyAction::#on_update) }; + } + + if attr.on_delete.is_some() { + let on_delete = attr + .on_delete + .as_ref() + .map(Self::parse_lit_string) + .ok_or_else(|| { + syn::Error::new_spanned(variant, "Missing value for 'on_delete'") + })??; + result = quote! { #result.on_delete(sea_orm::prelude::ForeignKeyAction::#on_delete) }; + } + result = quote! { #result.into() }; Result::<_, syn::Error>::Ok(result) diff --git a/tests/basic.rs b/tests/basic.rs index e62f1ad6..d617ba17 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -2,7 +2,7 @@ pub mod common; pub use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn}; -// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlit,runtime-async-std --test basic +// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlite,runtime-async-std-native-tls --test basic #[sea_orm_macros::test] #[cfg(feature = "sqlx-sqlite")] async fn main() { diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 9ab45905..ec3d3fb5 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -1,74 +1,27 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "baker")] pub struct Model { + #[sea_orm(primary_key)] pub id: i32, pub name: String, pub contact_details: Json, pub bakery_id: Option, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -pub enum Column { - Id, - Name, - ContactDetails, - BakeryId, -} - -#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] -pub enum PrimaryKey { - Id, -} - -impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] 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::ContactDetails => ColumnType::Json.def(), - Self::BakeryId => ColumnType::Integer.def().null(), - } - } -} - -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) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - } - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Bakery.def() diff --git a/tests/common/bakery_chain/bakery.rs b/tests/common/bakery_chain/bakery.rs index d03298b3..a168ad1c 100644 --- a/tests/common/bakery_chain/bakery.rs +++ b/tests/common/bakery_chain/bakery.rs @@ -1,70 +1,24 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "bakery")] pub struct Model { + #[sea_orm(primary_key)] 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 { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm(has_many = "super::baker::Entity")] Baker, + #[sea_orm(has_many = "super::order::Entity")] Order, + #[sea_orm(has_many = "super::cake::Entity")] 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::Double.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 for Entity { fn to() -> RelationDef { Relation::Baker.def() diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index cb1895a6..4730394b 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -1,82 +1,32 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "cake")] pub struct Model { + #[sea_orm(primary_key)] pub id: i32, pub name: String, + #[sea_orm(column_type = "Decimal(Some((19, 4)))")] pub price: Decimal, pub bakery_id: Option, pub gluten_free: bool, pub serial: Uuid, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -pub enum Column { - Id, - Name, - Price, - BakeryId, - GlutenFree, - Serial, -} - -#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] -pub enum PrimaryKey { - Id, -} - -impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Bakery, + #[sea_orm(has_many = "super::lineitem::Entity")] 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().null(), - Self::GlutenFree => ColumnType::Boolean.def(), - Self::Serial => ColumnType::Uuid.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) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), - } - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Bakery.def() diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index b0f52ec8..3582ada0 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -1,74 +1,32 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "cakes_bakers")] pub struct Model { + #[sea_orm(primary_key)] pub cake_id: i32, + #[sea_orm(primary_key)] 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 { - type ValueType = (i32, i32); - - fn auto_increment() -> bool { - false - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::cake::Entity", + from = "Column::CakeId", + to = "super::cake::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Cake, + #[sea_orm( + belongs_to = "super::baker::Entity", + from = "Column::BakerId", + to = "super::baker::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] 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) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - Self::Baker => Entity::belongs_to(super::baker::Entity) - .from(Column::BakerId) - .to(super::baker::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - } - } -} - impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/customer.rs b/tests/common/bakery_chain/customer.rs index 2cf1b9ec..31e41018 100644 --- a/tests/common/bakery_chain/customer.rs +++ b/tests/common/bakery_chain/customer.rs @@ -1,66 +1,21 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "customer")] pub struct Model { + #[sea_orm(primary_key)] pub id: i32, pub name: String, + #[sea_orm(column_type = "Text", nullable)] pub notes: Option, } -#[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 { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm(has_many = "super::order::Entity")] 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().null(), - } - } -} - -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - match self { - Self::Order => Entity::has_many(super::order::Entity).into(), - } - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Order.def() diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs index c89b44b8..c2d18987 100644 --- a/tests/common/bakery_chain/lineitem.rs +++ b/tests/common/bakery_chain/lineitem.rs @@ -1,84 +1,37 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "lineitem")] pub struct Model { + #[sea_orm(primary_key)] pub id: i32, + #[sea_orm(column_type = "Decimal(Some((19, 4)))")] pub price: Decimal, pub quantity: i32, pub order_id: i32, pub cake_id: 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 { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::order::Entity", + from = "Column::OrderId", + to = "super::order::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Order, + #[sea_orm( + belongs_to = "super::cake::Entity", + from = "Column::CakeId", + to = "super::cake::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Cake, } -impl ColumnTrait for Column { - type EntityName = Entity; - - fn def(&self) -> ColumnDef { - match self { - Self::Id => ColumnType::Integer.def(), - Self::Price => ColumnType::Decimal(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) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - Self::Cake => Entity::belongs_to(super::cake::Entity) - .from(Column::CakeId) - .to(super::cake::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - } - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Order.def() diff --git a/tests/common/bakery_chain/metadata.rs b/tests/common/bakery_chain/metadata.rs index ecef26c3..b9707059 100644 --- a/tests/common/bakery_chain/metadata.rs +++ b/tests/common/bakery_chain/metadata.rs @@ -1,57 +1,18 @@ use sea_orm::entity::prelude::*; use uuid::Uuid; -#[derive(Copy, Clone, Default, Debug, DeriveEntity)] -pub struct Entity; - -impl EntityName for Entity { - fn table_name(&self) -> &str { - "metadata" - } -} - -#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "metadata")] pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] pub uuid: Uuid, pub key: String, pub value: String, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -pub enum Column { - Uuid, - Key, - Value, -} - -#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] -pub enum PrimaryKey { - Uuid, -} - -impl PrimaryKeyTrait for PrimaryKey { - type ValueType = Uuid; - - fn auto_increment() -> bool { - false - } -} - #[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation {} -impl ColumnTrait for Column { - type EntityName = Entity; - - fn def(&self) -> ColumnDef { - match self { - Self::Uuid => ColumnType::Uuid.def(), - Self::Key => ColumnType::String(None).def(), - Self::Value => ColumnType::String(None).def(), - } - } -} - impl RelationTrait for Relation { fn def(&self) -> RelationDef { unreachable!() diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs index f9561b92..ed6185cf 100644 --- a/tests/common/bakery_chain/order.rs +++ b/tests/common/bakery_chain/order.rs @@ -1,86 +1,39 @@ 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)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "order")] pub struct Model { + #[sea_orm(primary_key)] pub id: i32, + #[sea_orm(column_type = "Decimal(Some((19, 4)))")] pub total: Decimal, pub bakery_id: i32, pub customer_id: i32, pub placed_at: DateTime, } -#[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 { - type ValueType = i32; - - fn auto_increment() -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Bakery, + #[sea_orm( + belongs_to = "super::customer::Entity", + from = "Column::CustomerId", + to = "super::customer::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] Customer, + #[sea_orm(has_many = "super::lineitem::Entity")] 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) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - Self::Customer => Entity::belongs_to(super::customer::Entity) - .from(Column::CustomerId) - .to(super::customer::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .into(), - Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), - } - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Bakery.def() diff --git a/tests/crud_tests.rs b/tests/crud_tests.rs index 7edb3500..a6078d8e 100644 --- a/tests/crud_tests.rs +++ b/tests/crud_tests.rs @@ -6,8 +6,9 @@ pub use crud::*; use sea_orm::DatabaseConnection; // Run the test locally: -// DATABASE_URL="mysql://root:root@localhost" cargo test --features sqlx-mysql,runtime-async-std --test crud_tests -// DATABASE_URL="postgres://root:root@localhost" cargo test --features sqlx-postgres,runtime-async-std --test crud_tests +// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlite,runtime-async-std-native-tls --test crud_tests +// DATABASE_URL="mysql://root:root@localhost" cargo test --features sqlx-mysql,runtime-async-std-native-tls --test crud_tests +// DATABASE_URL="postgres://root:root@localhost" cargo test --features sqlx-postgres,runtime-async-std-native-tls --test crud_tests #[sea_orm_macros::test] #[cfg(any( feature = "sqlx-mysql",