Cli serde skip deserialize for primary key option (#1186) (#1318)

* Cli serde skip deserialize for primary key option (#1186)

* Add CLI option to skip primary keys with serde

Implements: https://github.com/SeaQL/sea-orm/issues/841

* Codegen: fix tests

* complete skip_deserialize cli feature

* run fmt

* fix tests

Co-authored-by: witcher <witcher@wiredspace.de>

* [cli] should be `#[serde(skip_deserializing)]`

* [CLI] code refactor

* [cli] rename

Co-authored-by: Isaiah Gamble <77396670+tsar-boomba@users.noreply.github.com>
Co-authored-by: witcher <witcher@wiredspace.de>
This commit is contained in:
Billy Chan 2022-12-19 22:11:45 +08:00 committed by GitHub
parent 1f27837f49
commit 3f00725ee2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 17 deletions

View File

@ -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, '#[serde(skip_deserializing)]', for the primary key fields to skip them during deserialization, this flag will be affective only when '--with-serde' is 'both' or 'deserialize'"
)]
serde_skip_deserializing_primary_key: bool,
},
}

View File

@ -27,6 +27,7 @@ pub async fn run_generate_command(
with_copy_enums,
date_time_crate,
lib,
serde_skip_deserializing_primary_key,
} => {
if verbose {
let _ = tracing_subscriber::fmt()
@ -164,6 +165,7 @@ pub async fn run_generate_command(
date_time_crate.into(),
schema_name,
lib,
serde_skip_deserializing_primary_key,
);
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);

View File

@ -166,6 +166,22 @@ impl Entity {
// if exist, return nothing
.map_or(quote! {, Eq}, |_| quote! {})
}
pub fn get_serde_skip_deserializing(
&self,
serde_skip_deserializing_primary_key: bool,
) -> Vec<TokenStream> {
self.columns
.iter()
.map(|col| {
let is_primary_key = self.primary_keys.iter().any(|pk| pk.name == col.name);
col.get_serde_skip_deserializing(
is_primary_key,
serde_skip_deserializing_primary_key,
)
})
.collect()
}
}
#[cfg(test)]

View File

@ -211,6 +211,18 @@ impl Column {
}
info
}
pub fn get_serde_skip_deserializing(
&self,
is_primary_key: bool,
serde_skip_deserializing_primary_key: bool,
) -> TokenStream {
if serde_skip_deserializing_primary_key && is_primary_key {
quote! { #[serde(skip_deserializing)] }
} else {
quote! {}
}
}
}
impl From<ColumnDef> for Column {

View File

@ -43,6 +43,7 @@ pub struct EntityWriterContext {
pub(crate) date_time_crate: DateTimeCrate,
pub(crate) schema_name: Option<String>,
pub(crate) lib: bool,
pub(crate) serde_skip_deserializing_primary_key: bool,
}
impl WithSerde {
@ -103,6 +104,7 @@ impl EntityWriterContext {
date_time_crate: DateTimeCrate,
schema_name: Option<String>,
lib: bool,
serde_skip_deserializing_primary_key: bool,
) -> Self {
Self {
expanded_format,
@ -111,6 +113,7 @@ impl EntityWriterContext {
date_time_crate,
schema_name,
lib,
serde_skip_deserializing_primary_key,
}
}
}
@ -139,6 +142,11 @@ impl EntityWriter {
.iter()
.map(|column| column.get_info(&context.date_time_crate))
.collect::<Vec<String>>();
// use must have serde enabled to use this
let serde_skip_deserializing_primary_key = context
.serde_skip_deserializing_primary_key
&& (context.with_serde == WithSerde::Both
|| context.with_serde == WithSerde::Deserialize);
info!("Generating {}", entity_file);
for info in column_info.iter() {
@ -153,6 +161,7 @@ impl EntityWriter {
&context.with_serde,
&context.date_time_crate,
&context.schema_name,
serde_skip_deserializing_primary_key,
)
} else {
Self::gen_compact_code_blocks(
@ -160,6 +169,7 @@ impl EntityWriter {
&context.with_serde,
&context.date_time_crate,
&context.schema_name,
serde_skip_deserializing_primary_key,
)
};
Self::write(&mut lines, code_blocks);
@ -259,6 +269,7 @@ impl EntityWriter {
with_serde: &WithSerde,
date_time_crate: &DateTimeCrate,
schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool,
) -> Vec<TokenStream> {
let mut imports = Self::gen_import(with_serde);
imports.extend(Self::gen_import_active_enum(entity));
@ -266,7 +277,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,
serde_skip_deserializing_primary_key,
),
Self::gen_column_enum(entity),
Self::gen_primary_key_enum(entity),
Self::gen_impl_primary_key(entity, date_time_crate),
@ -285,12 +301,19 @@ impl EntityWriter {
with_serde: &WithSerde,
date_time_crate: &DateTimeCrate,
schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool,
) -> Vec<TokenStream> {
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,
serde_skip_deserializing_primary_key,
),
Self::gen_compact_relation_enum(entity),
];
code_blocks.extend(Self::gen_impl_related(entity));
@ -378,16 +401,22 @@ impl EntityWriter {
entity: &Entity,
with_serde: &WithSerde,
date_time_crate: &DateTimeCrate,
serde_skip_deserializing_primary_key: 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 serde_skip_deserializing =
entity.get_serde_skip_deserializing(serde_skip_deserializing_primary_key);
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,)*
#(
#serde_skip_deserializing
pub #column_names_snake_case: #column_rs_types,
)*
}
}
}
@ -566,6 +595,7 @@ impl EntityWriter {
with_serde: &WithSerde,
date_time_crate: &DateTimeCrate,
schema_name: &Option<String>,
serde_skip_deserializing_primary_key: bool,
) -> TokenStream {
let table_name = entity.table_name.as_str();
let column_names_snake_case = entity.get_column_names_snake_case();
@ -581,11 +611,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 });
@ -600,20 +631,25 @@ impl EntityWriter {
if col.unique {
attrs.push(quote! { unique });
}
let mut ts = quote! {};
if !attrs.is_empty() {
let mut ts = TokenStream::new();
for (i, attr) in attrs.into_iter().enumerate() {
if i > 0 {
ts = quote! { #ts, };
}
ts = quote! { #ts #attr };
}
quote! {
#[sea_orm(#ts)]
}
} else {
TokenStream::new()
ts = quote! { #[sea_orm(#ts)] };
}
let serde_skip_deserializing = col.get_serde_skip_deserializing(
is_primary_key,
serde_skip_deserializing_primary_key,
);
ts = quote! {
#ts
#serde_skip_deserializing
};
ts
})
.collect();
let schema_name = match Self::gen_schema_name(schema_name) {
@ -1260,7 +1296,8 @@ mod tests {
entity,
&crate::WithSerde::None,
&crate::DateTimeCrate::Chrono,
&None
&None,
false,
)
.into_iter()
.skip(1)
@ -1276,7 +1313,8 @@ mod tests {
entity,
&crate::WithSerde::None,
&crate::DateTimeCrate::Chrono,
&Some("public".to_owned())
&Some("public".to_owned()),
false,
)
.into_iter()
.skip(1)
@ -1292,7 +1330,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)
@ -1344,7 +1383,8 @@ mod tests {
entity,
&crate::WithSerde::None,
&crate::DateTimeCrate::Chrono,
&None
&None,
false,
)
.into_iter()
.skip(1)
@ -1360,7 +1400,8 @@ mod tests {
entity,
&crate::WithSerde::None,
&crate::DateTimeCrate::Chrono,
&Some("public".to_owned())
&Some("public".to_owned()),
false,
)
.into_iter()
.skip(1)
@ -1376,7 +1417,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)
@ -1481,11 +1523,13 @@ mod tests {
cake_entity: &Entity,
entity_serde_variant: &(String, WithSerde, Option<String>),
generator: Box<
dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option<String>) -> Vec<TokenStream>,
dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option<String>, bool) -> Vec<TokenStream>,
>,
) -> io::Result<()> {
let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
let mut lines: Vec<String> = Vec::new();
let serde_skip_deserializing_primary_key = entity_serde_variant.1 == WithSerde::Both
|| entity_serde_variant.1 == WithSerde::Deserialize;
reader.read_until(b'\n', &mut Vec::new())?;
@ -1496,11 +1540,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,
serde_skip_deserializing_primary_key,
)
.into_iter()
.fold(TokenStream::new(), |mut acc, tok| {

View File

@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,

View File

@ -7,6 +7,7 @@ use serde::Deserialize;
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,

View File

@ -14,6 +14,7 @@ impl EntityName for Entity {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)]
pub struct Model {
#[serde(skip_deserializing)]
pub id: i32,
pub name: Option<String> ,
}

View File

@ -14,6 +14,7 @@ impl EntityName for Entity {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Deserialize)]
pub struct Model {
#[serde(skip_deserializing)]
pub id: i32,
pub name: Option<String> ,
}