diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index edc43a374..8ff5707d1 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -228,6 +228,13 @@ pub enum GenerateSubcommands { help = "Generate index file as `lib.rs` instead of `mod.rs`." )] lib: bool, + + #[clap( + action, + long, + help = "Generate a serde field attribute for the primary keys to skip them during deserialization if they're not present, must also use '--with-serde' 'both' or 'deserialize'" + )] + skip_primary_key_deserialization: bool, }, } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index b1d0971b8..84f6a4187 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -27,6 +27,7 @@ pub async fn run_generate_command( with_copy_enums, date_time_crate, lib, + skip_primary_key_deserialization, } => { if verbose { let _ = tracing_subscriber::fmt() @@ -173,6 +174,7 @@ pub async fn run_generate_command( date_time_crate.into(), schema_name, lib, + skip_primary_key_deserialization, ); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 360c13fc1..97e268c1a 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -43,6 +43,7 @@ pub struct EntityWriterContext { pub(crate) date_time_crate: DateTimeCrate, pub(crate) schema_name: Option, pub(crate) lib: bool, + pub(crate) skip_primary_key_deserialization: bool, } impl WithSerde { @@ -103,6 +104,7 @@ impl EntityWriterContext { date_time_crate: DateTimeCrate, schema_name: Option, lib: bool, + skip_primary_key_deserialization: bool, ) -> Self { Self { expanded_format, @@ -111,6 +113,7 @@ impl EntityWriterContext { date_time_crate, schema_name, lib, + skip_primary_key_deserialization, } } } @@ -139,6 +142,10 @@ impl EntityWriter { .iter() .map(|column| column.get_info(&context.date_time_crate)) .collect::>(); + // use must have serde enabled to use this + let skip_primary_key_deserialization = context.skip_primary_key_deserialization + && (context.with_serde == WithSerde::Both + || context.with_serde == WithSerde::Deserialize); info!("Generating {}", entity_file); for info in column_info.iter() { @@ -153,6 +160,7 @@ impl EntityWriter { &context.with_serde, &context.date_time_crate, &context.schema_name, + skip_primary_key_deserialization, ) } else { Self::gen_compact_code_blocks( @@ -160,6 +168,7 @@ impl EntityWriter { &context.with_serde, &context.date_time_crate, &context.schema_name, + skip_primary_key_deserialization, ) }; Self::write(&mut lines, code_blocks); @@ -259,6 +268,7 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + skip_primary_key_deserialization: bool, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); @@ -266,7 +276,12 @@ impl EntityWriter { imports, Self::gen_entity_struct(), Self::gen_impl_entity_name(entity, schema_name), - Self::gen_model_struct(entity, with_serde, date_time_crate), + Self::gen_model_struct( + entity, + with_serde, + date_time_crate, + skip_primary_key_deserialization, + ), Self::gen_column_enum(entity), Self::gen_primary_key_enum(entity), Self::gen_impl_primary_key(entity, date_time_crate), @@ -285,12 +300,19 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + skip_primary_key_deserialization: bool, ) -> Vec { let mut imports = Self::gen_import(with_serde); imports.extend(Self::gen_import_active_enum(entity)); let mut code_blocks = vec![ imports, - Self::gen_compact_model_struct(entity, with_serde, date_time_crate, schema_name), + Self::gen_compact_model_struct( + entity, + with_serde, + date_time_crate, + schema_name, + skip_primary_key_deserialization, + ), Self::gen_compact_relation_enum(entity), ]; code_blocks.extend(Self::gen_impl_related(entity)); @@ -378,16 +400,42 @@ impl EntityWriter { entity: &Entity, with_serde: &WithSerde, date_time_crate: &DateTimeCrate, + skip_primary_key_deserialization: bool, ) -> TokenStream { let column_names_snake_case = entity.get_column_names_snake_case(); let column_rs_types = entity.get_column_rs_types(date_time_crate); let if_eq_needed = entity.get_eq_needed(); + let primary_keys: Vec = entity + .primary_keys + .iter() + .map(|pk| pk.name.clone()) + .collect(); + let fields = column_names_snake_case.into_iter().enumerate().fold( + TokenStream::new(), + |tokens, (i, field_name)| { + let field_type = column_rs_types.get(i).unwrap(); + let is_primary_key = primary_keys.contains(&field_name.to_string()); + if is_primary_key && skip_primary_key_deserialization { + quote! { + #tokens + #[serde(skip_deserialization)] + pub #field_name: #field_type, + } + } else { + quote! { + #tokens + pub #field_name: #field_type, + } + } + }, + ); + let extra_derive = with_serde.extra_derive(); quote! { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive)] pub struct Model { - #(pub #column_names_snake_case: #column_rs_types,)* + #fields } } } @@ -566,6 +614,7 @@ impl EntityWriter { with_serde: &WithSerde, date_time_crate: &DateTimeCrate, schema_name: &Option, + skip_primary_key_deserialization: bool, ) -> TokenStream { let table_name = entity.table_name.as_str(); let column_names_snake_case = entity.get_column_names_snake_case(); @@ -581,11 +630,12 @@ impl EntityWriter { .iter() .map(|col| { let mut attrs: Punctuated<_, Comma> = Punctuated::new(); + let is_primary_key = primary_keys.contains(&col.name); if !col.is_snake_case_name() { let column_name = &col.name; attrs.push(quote! { column_name = #column_name }); } - if primary_keys.contains(&col.name) { + if is_primary_key { attrs.push(quote! { primary_key }); if !col.auto_increment { attrs.push(quote! { auto_increment = false }); @@ -608,8 +658,15 @@ impl EntityWriter { } ts = quote! { #ts #attr }; } - quote! { - #[sea_orm(#ts)] + if is_primary_key && skip_primary_key_deserialization { + quote! { + #[sea_orm(#ts)] + #[serde(skip_deserialization)] + } + } else { + quote! { + #[sea_orm(#ts)] + } } } else { TokenStream::new() @@ -1247,7 +1304,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &None + &None, + false, ) .into_iter() .skip(1) @@ -1263,7 +1321,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("public".to_owned()) + &Some("public".to_owned()), + false, ) .into_iter() .skip(1) @@ -1279,7 +1338,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("schema_name".to_owned()) + &Some("schema_name".to_owned()), + false, ) .into_iter() .skip(1) @@ -1331,7 +1391,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &None + &None, + false, ) .into_iter() .skip(1) @@ -1347,7 +1408,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("public".to_owned()) + &Some("public".to_owned()), + false, ) .into_iter() .skip(1) @@ -1363,7 +1425,8 @@ mod tests { entity, &crate::WithSerde::None, &crate::DateTimeCrate::Chrono, - &Some("schema_name".to_owned()) + &Some("schema_name".to_owned()), + false, ) .into_iter() .skip(1) @@ -1393,6 +1456,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_compact_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1402,6 +1466,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_compact_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1411,6 +1476,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_compact_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1420,6 +1486,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_compact_code_blocks), + false, )?; // Expanded code blocks @@ -1431,6 +1498,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_expanded_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1440,6 +1508,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_expanded_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1449,6 +1518,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_expanded_code_blocks), + false, )?; assert_serde_variant_results( &cake_entity, @@ -1458,6 +1528,7 @@ mod tests { None, ), Box::new(EntityWriter::gen_expanded_code_blocks), + false, )?; Ok(()) @@ -1468,11 +1539,14 @@ mod tests { cake_entity: &Entity, entity_serde_variant: &(String, WithSerde, Option), generator: Box< - dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option) -> Vec, + dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option, bool) -> Vec, >, + primary_key_auto_increment: bool, ) -> io::Result<()> { let mut reader = BufReader::new(entity_serde_variant.0.as_bytes()); let mut lines: Vec = Vec::new(); + let skip_primary_key_deserialization = entity_serde_variant.1 == WithSerde::Both + || entity_serde_variant.1 == WithSerde::Deserialize; reader.read_until(b'\n', &mut Vec::new())?; @@ -1483,11 +1557,13 @@ mod tests { } let content = lines.join(""); let expected: TokenStream = content.parse().unwrap(); + println!("{:?}", entity_serde_variant.1); let generated = generator( cake_entity, &entity_serde_variant.1, &DateTimeCrate::Chrono, &entity_serde_variant.2, + skip_primary_key_deserialization, ) .into_iter() .fold(TokenStream::new(), |mut acc, tok| { diff --git a/sea-orm-codegen/tests/compact_with_serde/cake_both.rs b/sea-orm-codegen/tests/compact_with_serde/cake_both.rs index 54d3cd167..874d24aad 100644 --- a/sea-orm-codegen/tests/compact_with_serde/cake_both.rs +++ b/sea-orm-codegen/tests/compact_with_serde/cake_both.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; #[sea_orm(table_name = "cake")] pub struct Model { #[sea_orm(primary_key)] + #[serde(skip_deserialization)] pub id: i32, #[sea_orm(column_type = "Text", nullable)] pub name: Option , diff --git a/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs b/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs index f11569e4a..e2c789086 100644 --- a/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs +++ b/sea-orm-codegen/tests/compact_with_serde/cake_deserialize.rs @@ -7,6 +7,7 @@ use serde::Deserialize; #[sea_orm(table_name = "cake")] pub struct Model { #[sea_orm(primary_key)] + #[serde(skip_deserialization)] pub id: i32, #[sea_orm(column_type = "Text", nullable)] pub name: Option , diff --git a/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs b/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs index 924887b42..d0472a974 100644 --- a/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs +++ b/sea-orm-codegen/tests/expanded_with_serde/cake_both.rs @@ -14,6 +14,7 @@ impl EntityName for Entity { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)] pub struct Model { + #[serde(skip_deserialization)] pub id: i32, pub name: Option , } diff --git a/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs b/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs index 88a7c3a96..378077d83 100644 --- a/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs +++ b/sea-orm-codegen/tests/expanded_with_serde/cake_deserialize.rs @@ -14,6 +14,7 @@ impl EntityName for Entity { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Deserialize)] pub struct Model { + #[serde(skip_deserialization)] pub id: i32, pub name: Option , }