From 880dc9340efcb1402429cc08a1c7e3093fc7b9e3 Mon Sep 17 00:00:00 2001 From: Huliiiiii Date: Sat, 25 Jul 2026 02:12:40 +0800 Subject: [PATCH] Add `Send` bounds for transactions in `ActiveModelEx` methods --- sea-orm-macros/src/derives/active_model_ex.rs | 30 ++++++++++---- ...derive_active_model_ex_send_future_test.rs | 40 +++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 sea-orm-macros/tests/derive_active_model_ex_send_future_test.rs diff --git a/sea-orm-macros/src/derives/active_model_ex.rs b/sea-orm-macros/src/derives/active_model_ex.rs index b095a2836..b8755f382 100644 --- a/sea-orm-macros/src/derives/active_model_ex.rs +++ b/sea-orm-macros/src/derives/active_model_ex.rs @@ -10,6 +10,20 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use syn::{Attribute, Data, LitStr, PathArguments, Type, TypePath, Visibility}; +fn transaction_trait_bounds() -> TokenStream { + if cfg!(feature = "async") { + quote! { + C: sea_orm::TransactionTrait + std::marker::Sync, + ::Transaction: + sea_orm::TransactionTrait< + Transaction = ::Transaction, + > + std::marker::Send, + } + } else { + quote!(C: sea_orm::TransactionTrait,) + } +} + enum RelationAttr { BelongsTo { from: RelationColumns, @@ -676,6 +690,7 @@ impl ActiveModelActionTokens { fn expand(self) -> TokenStream { let async_ = async_token(); let await_ = await_token(); + let transaction_trait_bounds = transaction_trait_bounds(); let Self { belongs_to_action, belongs_to_after_action, @@ -694,7 +709,7 @@ impl ActiveModelActionTokens { #[doc = " Generated by sea-orm-macros"] pub #async_ fn insert<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { let active_model = self.action(sea_orm::ActiveModelAction::Insert, db)#await_?; active_model.try_into() @@ -703,7 +718,7 @@ impl ActiveModelActionTokens { #[doc = " Generated by sea-orm-macros"] pub #async_ fn update<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { let active_model = self.action(sea_orm::ActiveModelAction::Update, db)#await_?; active_model.try_into() @@ -712,7 +727,7 @@ impl ActiveModelActionTokens { #[doc = " Generated by sea-orm-macros"] pub #async_ fn save<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { self.action(sea_orm::ActiveModelAction::Save, db)#await_ } @@ -720,7 +735,7 @@ impl ActiveModelActionTokens { #[doc = " Generated by sea-orm-macros"] pub #async_ fn delete<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { use sea_orm::{IntoActiveModel, TransactionSession}; @@ -743,7 +758,7 @@ impl ActiveModelActionTokens { #[doc = " Generated by sea-orm-macros"] pub #async_ fn action<'a, C>(mut self, action: sea_orm::ActiveModelAction, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { use sea_orm::{ActiveBelongsTo, ActiveHasOne, ActiveHasMany, IntoActiveModel, TransactionSession}; let txn = db.begin()#await_?; @@ -827,6 +842,7 @@ fn expand_active_model_ex<'a>( ) -> syn::Result { let async_ = async_token(); let await_ = await_token(); + let transaction_trait_bounds = transaction_trait_bounds(); let active_model_trait_methods = DeriveActiveModel::new(vis, ident, data)?.impl_active_model_trait_methods(); let Output { @@ -949,7 +965,7 @@ fn expand_active_model_ex<'a>( #[doc = " Generated by sea-orm-macros"] pub #async_ fn cascade_delete<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { self.into_ex().delete(db)#await_ } @@ -959,7 +975,7 @@ fn expand_active_model_ex<'a>( #[doc = " Generated by sea-orm-macros"] pub #async_ fn delete<'a, C>(self, db: &'a C) -> Result where - C: sea_orm::TransactionTrait, + #transaction_trait_bounds { let active_model: ActiveModelEx = self.into(); active_model.delete(db)#await_ diff --git a/sea-orm-macros/tests/derive_active_model_ex_send_future_test.rs b/sea-orm-macros/tests/derive_active_model_ex_send_future_test.rs new file mode 100644 index 000000000..9bd2b673f --- /dev/null +++ b/sea-orm-macros/tests/derive_active_model_ex_send_future_test.rs @@ -0,0 +1,40 @@ +#![deny(clippy::future_not_send)] + +mod parent { + use sea_orm::entity::prelude::*; + + #[sea_orm::model] + #[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "parent")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(has_many)] + pub children: HasMany, + } + + impl ActiveModelBehavior for ActiveModel {} +} + +mod child { + use sea_orm::entity::prelude::*; + + #[sea_orm::model] + #[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "child")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub parent_id: Option, + #[sea_orm(belongs_to, from = "parent_id", to = "id")] + pub parent: BelongsTo>, + } + + impl ActiveModelBehavior for ActiveModel {} +} + +#[test] +fn generated_active_model_ex_mutation_futures_are_send() { + let _ = parent::ActiveModel::builder(); + let _ = child::ActiveModel::builder(); +}