Skip to content

Commit 5773b30

Browse files
committed
fix: handle nested Option<Model> in FromQueryResult missing struct for test
1 parent 5b37411 commit 5773b30

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

sea-orm-macros/src/derives/from_query_result.rs

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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,26 @@ struct TryFromQueryResultCheck<'a>(bool, &'a FromQueryResultItem);
4244

4345
impl 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+
quote! { "" }
62+
} else {
63+
quote! { pre }
64+
};
5265
tokens.extend(quote! {
53-
let #ident = match row.try_get_nullable(pre, #name) {
66+
let #ident = match row.try_get_nullable(#prefix_to_use, #name) {
5467
Err(v @ sea_orm::TryGetError::DbErr(_)) => {
5568
return Err(v);
5669
}
@@ -64,14 +77,32 @@ impl ToTokens for TryFromQueryResultCheck<'_> {
6477
});
6578
}
6679
ItemType::Nested => {
67-
let prefix = if self.0 {
80+
let prefix_str = if self.1.prefix {
6881
let name = ident.unraw().to_string();
6982
quote! { &format!("{pre}{}_", #name) }
83+
} else if self.0 {
84+
let is_option = if let syn::Type::Path(type_path) = field_type {
85+
type_path
86+
.path
87+
.segments
88+
.last()
89+
.map(|seg| seg.ident == "Option")
90+
.unwrap_or(false)
91+
} else {
92+
false
93+
};
94+
95+
if is_option {
96+
let name = ident.unraw().to_string();
97+
quote! { &format!("{pre}{}_", #name) }
98+
} else {
99+
quote! { pre }
100+
}
70101
} else {
71102
quote! { pre }
72103
};
73104
tokens.extend(quote! {
74-
let #ident = match sea_orm::FromQueryResult::from_query_result_nullable(row, #prefix) {
105+
let #ident = match sea_orm::FromQueryResult::from_query_result_nullable(row, #prefix_str) {
75106
Err(v @ sea_orm::TryGetError::DbErr(_)) => {
76107
return Err(v);
77108
}
@@ -87,14 +118,45 @@ struct TryFromQueryResultAssignment<'a>(&'a FromQueryResultItem);
87118

88119
impl ToTokens for TryFromQueryResultAssignment<'_> {
89120
fn to_tokens(&self, tokens: &mut TokenStream) {
90-
let FromQueryResultItem { ident, typ, .. } = self.0;
121+
let FromQueryResultItem {
122+
ident,
123+
typ,
124+
field_type,
125+
..
126+
} = self.0;
91127

92128
match typ {
93-
ItemType::Flat | ItemType::Nested => {
129+
ItemType::Flat => {
94130
tokens.extend(quote! {
95131
#ident: #ident?,
96132
});
97133
}
134+
ItemType::Nested => {
135+
let is_option = if let syn::Type::Path(type_path) = field_type {
136+
type_path
137+
.path
138+
.segments
139+
.last()
140+
.map(|seg| seg.ident == "Option")
141+
.unwrap_or(false)
142+
} else {
143+
false
144+
};
145+
146+
if is_option {
147+
tokens.extend(quote! {
148+
#ident: match #ident {
149+
Ok(v) => Some(v),
150+
Err(sea_orm::TryGetError::Null(_)) => None,
151+
Err(e) => return Err(e),
152+
},
153+
});
154+
} else {
155+
tokens.extend(quote! {
156+
#ident: #ident?,
157+
});
158+
}
159+
}
98160
ItemType::Skip => {
99161
tokens.extend(quote! {
100162
#ident,
@@ -125,6 +187,7 @@ impl DeriveFromQueryResult {
125187
for parsed_field in parsed_fields {
126188
let mut typ = ItemType::Flat;
127189
let mut alias = None;
190+
let mut prefix = false;
128191
for attr in parsed_field.attrs.iter() {
129192
if !attr.path().is_ident("sea_orm") {
130193
continue;
@@ -136,6 +199,8 @@ impl DeriveFromQueryResult {
136199
typ = ItemType::Skip;
137200
} else if meta.exists("nested") {
138201
typ = ItemType::Nested;
202+
} else if meta.exists("prefix") {
203+
prefix = true;
139204
} else if let Some(alias_) = meta.get_as_kv("from_alias") {
140205
alias = Some(alias_);
141206
} else {
@@ -145,7 +210,14 @@ impl DeriveFromQueryResult {
145210
}
146211
}
147212
let ident = format_ident!("{}", parsed_field.ident.unwrap().to_string());
148-
fields.push(FromQueryResultItem { typ, ident, alias });
213+
let field_type = parsed_field.ty;
214+
fields.push(FromQueryResultItem {
215+
typ,
216+
ident,
217+
alias,
218+
field_type,
219+
prefix,
220+
});
149221
}
150222

151223
Ok(Self {

sea-orm-macros/src/derives/partial_model.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ impl DerivePartialModel {
213213
}
214214
.to_owned(),
215215
alias: None,
216+
field_type: match col_as {
217+
ColumnAs::Nested { typ, .. } => typ.clone(),
218+
_ => syn::parse_quote!(()),
219+
},
220+
prefix: false,
216221
})
217222
.collect(),
218223
}

tests/from_query_result_tests.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ struct BakeryFlat {
4949
profit: f64,
5050
}
5151

52+
#[derive(FromQueryResult)]
53+
struct CakeWithOptionalBakeryModel {
54+
id: i32,
55+
name: String,
56+
#[sea_orm(nested, prefix)]
57+
bakery: Option<bakery::Model>,
58+
}
59+
5260
#[sea_orm_macros::test]
5361
async fn from_query_result_left_join_does_not_exist() {
5462
let ctx = TestContext::new("from_query_result_left_join_does_not_exist").await;
@@ -89,9 +97,9 @@ async fn from_query_result_left_join_with_optional_model_does_not_exist() {
8997
.select_only()
9098
.column(cake::Column::Id)
9199
.column(cake::Column::Name)
92-
.column(bakery::Column::Id)
93-
.column(bakery::Column::Name)
94-
.column(bakery::Column::ProfitMargin)
100+
.column_as(bakery::Column::Id, "bakery_id")
101+
.column_as(bakery::Column::Name, "bakery_name")
102+
.column_as(bakery::Column::ProfitMargin, "bakery_profit_margin")
95103
.left_join(bakery::Entity)
96104
.order_by_asc(cake::Column::Id)
97105
.into_model()

0 commit comments

Comments
 (0)