@@ -27,6 +27,8 @@ pub(super) struct FromQueryResultItem {
2727 pub typ : ItemType ,
2828 pub ident : Ident ,
2929 pub alias : Option < String > ,
30+ pub field_type : syn:: Type ,
31+ pub prefix : bool ,
3032}
3133
3234/// Initially, we try to obtain the value for each field and check if it is an ordinary DB error
@@ -42,15 +44,27 @@ struct TryFromQueryResultCheck<'a>(bool, &'a FromQueryResultItem);
4244
4345impl ToTokens for TryFromQueryResultCheck < ' _ > {
4446 fn to_tokens ( & self , tokens : & mut TokenStream ) {
45- let FromQueryResultItem { ident, typ, alias } = self . 1 ;
47+ let FromQueryResultItem {
48+ ident,
49+ typ,
50+ alias,
51+ field_type,
52+ ..
53+ } = self . 1 ;
4654
4755 match typ {
4856 ItemType :: Flat => {
4957 let name = alias
5058 . to_owned ( )
5159 . unwrap_or_else ( || ident. unraw ( ) . to_string ( ) ) ;
60+ let prefix_to_use = if alias. is_some ( ) {
61+ // If there's an explicit alias, use it as-is without prefix
62+ quote ! { "" }
63+ } else {
64+ quote ! { pre }
65+ } ;
5266 tokens. extend ( quote ! {
53- let #ident = match row. try_get_nullable( pre , #name) {
67+ let #ident = match row. try_get_nullable( #prefix_to_use , #name) {
5468 Err ( v @ sea_orm:: TryGetError :: DbErr ( _) ) => {
5569 return Err ( v) ;
5670 }
@@ -64,14 +78,35 @@ impl ToTokens for TryFromQueryResultCheck<'_> {
6478 } ) ;
6579 }
6680 ItemType :: Nested => {
67- let prefix = if self . 0 {
81+ let prefix_str = if self . 1 . prefix {
82+ // Use field name as prefix if #[sea_orm(prefix)] is specified
6883 let name = ident. unraw ( ) . to_string ( ) ;
6984 quote ! { & format!( "{pre}{}_" , #name) }
85+ } else if self . 0 {
86+ // For PartialModel (when self.0 is true), check if field is Option<T>
87+ // and add automatic prefix for optional nested fields
88+ let is_option = if let syn:: Type :: Path ( type_path) = field_type {
89+ type_path
90+ . path
91+ . segments
92+ . last ( )
93+ . map ( |seg| seg. ident == "Option" )
94+ . unwrap_or ( false )
95+ } else {
96+ false
97+ } ;
98+
99+ if is_option {
100+ let name = ident. unraw ( ) . to_string ( ) ;
101+ quote ! { & format!( "{pre}{}_" , #name) }
102+ } else {
103+ quote ! { pre }
104+ }
70105 } else {
71106 quote ! { pre }
72107 } ;
73108 tokens. extend ( quote ! {
74- let #ident = match sea_orm:: FromQueryResult :: from_query_result_nullable( row, #prefix ) {
109+ let #ident = match sea_orm:: FromQueryResult :: from_query_result_nullable( row, #prefix_str ) {
75110 Err ( v @ sea_orm:: TryGetError :: DbErr ( _) ) => {
76111 return Err ( v) ;
77112 }
@@ -87,14 +122,45 @@ struct TryFromQueryResultAssignment<'a>(&'a FromQueryResultItem);
87122
88123impl ToTokens for TryFromQueryResultAssignment < ' _ > {
89124 fn to_tokens ( & self , tokens : & mut TokenStream ) {
90- let FromQueryResultItem { ident, typ, .. } = self . 0 ;
125+ let FromQueryResultItem {
126+ ident,
127+ typ,
128+ field_type,
129+ ..
130+ } = self . 0 ;
91131
92132 match typ {
93- ItemType :: Flat | ItemType :: Nested => {
133+ ItemType :: Flat => {
94134 tokens. extend ( quote ! {
95135 #ident: #ident?,
96136 } ) ;
97137 }
138+ ItemType :: Nested => {
139+ let is_option = if let syn:: Type :: Path ( type_path) = field_type {
140+ type_path
141+ . path
142+ . segments
143+ . last ( )
144+ . map ( |seg| seg. ident == "Option" )
145+ . unwrap_or ( false )
146+ } else {
147+ false
148+ } ;
149+
150+ if is_option {
151+ tokens. extend ( quote ! {
152+ #ident: match #ident {
153+ Ok ( v) => Some ( v) ,
154+ Err ( sea_orm:: TryGetError :: Null ( _) ) => None ,
155+ Err ( e) => return Err ( e) ,
156+ } ,
157+ } ) ;
158+ } else {
159+ tokens. extend ( quote ! {
160+ #ident: #ident?,
161+ } ) ;
162+ }
163+ }
98164 ItemType :: Skip => {
99165 tokens. extend ( quote ! {
100166 #ident,
@@ -125,6 +191,7 @@ impl DeriveFromQueryResult {
125191 for parsed_field in parsed_fields {
126192 let mut typ = ItemType :: Flat ;
127193 let mut alias = None ;
194+ let mut prefix = false ;
128195 for attr in parsed_field. attrs . iter ( ) {
129196 if !attr. path ( ) . is_ident ( "sea_orm" ) {
130197 continue ;
@@ -136,6 +203,8 @@ impl DeriveFromQueryResult {
136203 typ = ItemType :: Skip ;
137204 } else if meta. exists ( "nested" ) {
138205 typ = ItemType :: Nested ;
206+ } else if meta. exists ( "prefix" ) {
207+ prefix = true ;
139208 } else if let Some ( alias_) = meta. get_as_kv ( "from_alias" ) {
140209 alias = Some ( alias_) ;
141210 } else {
@@ -145,7 +214,14 @@ impl DeriveFromQueryResult {
145214 }
146215 }
147216 let ident = format_ident ! ( "{}" , parsed_field. ident. unwrap( ) . to_string( ) ) ;
148- fields. push ( FromQueryResultItem { typ, ident, alias } ) ;
217+ let field_type = parsed_field. ty ;
218+ fields. push ( FromQueryResultItem {
219+ typ,
220+ ident,
221+ alias,
222+ field_type,
223+ prefix,
224+ } ) ;
149225 }
150226
151227 Ok ( Self {
0 commit comments