* 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:
parent
1f27837f49
commit
3f00725ee2
@ -228,6 +228,13 @@ pub enum GenerateSubcommands {
|
|||||||
help = "Generate index file as `lib.rs` instead of `mod.rs`."
|
help = "Generate index file as `lib.rs` instead of `mod.rs`."
|
||||||
)]
|
)]
|
||||||
lib: bool,
|
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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ pub async fn run_generate_command(
|
|||||||
with_copy_enums,
|
with_copy_enums,
|
||||||
date_time_crate,
|
date_time_crate,
|
||||||
lib,
|
lib,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
} => {
|
} => {
|
||||||
if verbose {
|
if verbose {
|
||||||
let _ = tracing_subscriber::fmt()
|
let _ = tracing_subscriber::fmt()
|
||||||
@ -164,6 +165,7 @@ pub async fn run_generate_command(
|
|||||||
date_time_crate.into(),
|
date_time_crate.into(),
|
||||||
schema_name,
|
schema_name,
|
||||||
lib,
|
lib,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
);
|
);
|
||||||
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);
|
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);
|
||||||
|
|
||||||
|
@ -166,6 +166,22 @@ impl Entity {
|
|||||||
// if exist, return nothing
|
// if exist, return nothing
|
||||||
.map_or(quote! {, Eq}, |_| quote! {})
|
.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)]
|
#[cfg(test)]
|
||||||
|
@ -211,6 +211,18 @@ impl Column {
|
|||||||
}
|
}
|
||||||
info
|
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 {
|
impl From<ColumnDef> for Column {
|
||||||
|
@ -43,6 +43,7 @@ pub struct EntityWriterContext {
|
|||||||
pub(crate) date_time_crate: DateTimeCrate,
|
pub(crate) date_time_crate: DateTimeCrate,
|
||||||
pub(crate) schema_name: Option<String>,
|
pub(crate) schema_name: Option<String>,
|
||||||
pub(crate) lib: bool,
|
pub(crate) lib: bool,
|
||||||
|
pub(crate) serde_skip_deserializing_primary_key: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithSerde {
|
impl WithSerde {
|
||||||
@ -103,6 +104,7 @@ impl EntityWriterContext {
|
|||||||
date_time_crate: DateTimeCrate,
|
date_time_crate: DateTimeCrate,
|
||||||
schema_name: Option<String>,
|
schema_name: Option<String>,
|
||||||
lib: bool,
|
lib: bool,
|
||||||
|
serde_skip_deserializing_primary_key: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
expanded_format,
|
expanded_format,
|
||||||
@ -111,6 +113,7 @@ impl EntityWriterContext {
|
|||||||
date_time_crate,
|
date_time_crate,
|
||||||
schema_name,
|
schema_name,
|
||||||
lib,
|
lib,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,6 +142,11 @@ impl EntityWriter {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|column| column.get_info(&context.date_time_crate))
|
.map(|column| column.get_info(&context.date_time_crate))
|
||||||
.collect::<Vec<String>>();
|
.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);
|
info!("Generating {}", entity_file);
|
||||||
for info in column_info.iter() {
|
for info in column_info.iter() {
|
||||||
@ -153,6 +161,7 @@ impl EntityWriter {
|
|||||||
&context.with_serde,
|
&context.with_serde,
|
||||||
&context.date_time_crate,
|
&context.date_time_crate,
|
||||||
&context.schema_name,
|
&context.schema_name,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::gen_compact_code_blocks(
|
Self::gen_compact_code_blocks(
|
||||||
@ -160,6 +169,7 @@ impl EntityWriter {
|
|||||||
&context.with_serde,
|
&context.with_serde,
|
||||||
&context.date_time_crate,
|
&context.date_time_crate,
|
||||||
&context.schema_name,
|
&context.schema_name,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Self::write(&mut lines, code_blocks);
|
Self::write(&mut lines, code_blocks);
|
||||||
@ -259,6 +269,7 @@ impl EntityWriter {
|
|||||||
with_serde: &WithSerde,
|
with_serde: &WithSerde,
|
||||||
date_time_crate: &DateTimeCrate,
|
date_time_crate: &DateTimeCrate,
|
||||||
schema_name: &Option<String>,
|
schema_name: &Option<String>,
|
||||||
|
serde_skip_deserializing_primary_key: bool,
|
||||||
) -> Vec<TokenStream> {
|
) -> Vec<TokenStream> {
|
||||||
let mut imports = Self::gen_import(with_serde);
|
let mut imports = Self::gen_import(with_serde);
|
||||||
imports.extend(Self::gen_import_active_enum(entity));
|
imports.extend(Self::gen_import_active_enum(entity));
|
||||||
@ -266,7 +277,12 @@ impl EntityWriter {
|
|||||||
imports,
|
imports,
|
||||||
Self::gen_entity_struct(),
|
Self::gen_entity_struct(),
|
||||||
Self::gen_impl_entity_name(entity, schema_name),
|
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_column_enum(entity),
|
||||||
Self::gen_primary_key_enum(entity),
|
Self::gen_primary_key_enum(entity),
|
||||||
Self::gen_impl_primary_key(entity, date_time_crate),
|
Self::gen_impl_primary_key(entity, date_time_crate),
|
||||||
@ -285,12 +301,19 @@ impl EntityWriter {
|
|||||||
with_serde: &WithSerde,
|
with_serde: &WithSerde,
|
||||||
date_time_crate: &DateTimeCrate,
|
date_time_crate: &DateTimeCrate,
|
||||||
schema_name: &Option<String>,
|
schema_name: &Option<String>,
|
||||||
|
serde_skip_deserializing_primary_key: bool,
|
||||||
) -> Vec<TokenStream> {
|
) -> Vec<TokenStream> {
|
||||||
let mut imports = Self::gen_import(with_serde);
|
let mut imports = Self::gen_import(with_serde);
|
||||||
imports.extend(Self::gen_import_active_enum(entity));
|
imports.extend(Self::gen_import_active_enum(entity));
|
||||||
let mut code_blocks = vec![
|
let mut code_blocks = vec![
|
||||||
imports,
|
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),
|
Self::gen_compact_relation_enum(entity),
|
||||||
];
|
];
|
||||||
code_blocks.extend(Self::gen_impl_related(entity));
|
code_blocks.extend(Self::gen_impl_related(entity));
|
||||||
@ -378,16 +401,22 @@ impl EntityWriter {
|
|||||||
entity: &Entity,
|
entity: &Entity,
|
||||||
with_serde: &WithSerde,
|
with_serde: &WithSerde,
|
||||||
date_time_crate: &DateTimeCrate,
|
date_time_crate: &DateTimeCrate,
|
||||||
|
serde_skip_deserializing_primary_key: bool,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let column_names_snake_case = entity.get_column_names_snake_case();
|
let column_names_snake_case = entity.get_column_names_snake_case();
|
||||||
let column_rs_types = entity.get_column_rs_types(date_time_crate);
|
let column_rs_types = entity.get_column_rs_types(date_time_crate);
|
||||||
let if_eq_needed = entity.get_eq_needed();
|
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();
|
let extra_derive = with_serde.extra_derive();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive)]
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel #if_eq_needed #extra_derive)]
|
||||||
pub struct Model {
|
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,
|
with_serde: &WithSerde,
|
||||||
date_time_crate: &DateTimeCrate,
|
date_time_crate: &DateTimeCrate,
|
||||||
schema_name: &Option<String>,
|
schema_name: &Option<String>,
|
||||||
|
serde_skip_deserializing_primary_key: bool,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let table_name = entity.table_name.as_str();
|
let table_name = entity.table_name.as_str();
|
||||||
let column_names_snake_case = entity.get_column_names_snake_case();
|
let column_names_snake_case = entity.get_column_names_snake_case();
|
||||||
@ -581,11 +611,12 @@ impl EntityWriter {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|col| {
|
.map(|col| {
|
||||||
let mut attrs: Punctuated<_, Comma> = Punctuated::new();
|
let mut attrs: Punctuated<_, Comma> = Punctuated::new();
|
||||||
|
let is_primary_key = primary_keys.contains(&col.name);
|
||||||
if !col.is_snake_case_name() {
|
if !col.is_snake_case_name() {
|
||||||
let column_name = &col.name;
|
let column_name = &col.name;
|
||||||
attrs.push(quote! { column_name = #column_name });
|
attrs.push(quote! { column_name = #column_name });
|
||||||
}
|
}
|
||||||
if primary_keys.contains(&col.name) {
|
if is_primary_key {
|
||||||
attrs.push(quote! { primary_key });
|
attrs.push(quote! { primary_key });
|
||||||
if !col.auto_increment {
|
if !col.auto_increment {
|
||||||
attrs.push(quote! { auto_increment = false });
|
attrs.push(quote! { auto_increment = false });
|
||||||
@ -600,20 +631,25 @@ impl EntityWriter {
|
|||||||
if col.unique {
|
if col.unique {
|
||||||
attrs.push(quote! { unique });
|
attrs.push(quote! { unique });
|
||||||
}
|
}
|
||||||
|
let mut ts = quote! {};
|
||||||
if !attrs.is_empty() {
|
if !attrs.is_empty() {
|
||||||
let mut ts = TokenStream::new();
|
|
||||||
for (i, attr) in attrs.into_iter().enumerate() {
|
for (i, attr) in attrs.into_iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
ts = quote! { #ts, };
|
ts = quote! { #ts, };
|
||||||
}
|
}
|
||||||
ts = quote! { #ts #attr };
|
ts = quote! { #ts #attr };
|
||||||
}
|
}
|
||||||
quote! {
|
ts = quote! { #[sea_orm(#ts)] };
|
||||||
#[sea_orm(#ts)]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TokenStream::new()
|
|
||||||
}
|
}
|
||||||
|
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();
|
.collect();
|
||||||
let schema_name = match Self::gen_schema_name(schema_name) {
|
let schema_name = match Self::gen_schema_name(schema_name) {
|
||||||
@ -1260,7 +1296,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&None
|
&None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1276,7 +1313,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&Some("public".to_owned())
|
&Some("public".to_owned()),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1292,7 +1330,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&Some("schema_name".to_owned())
|
&Some("schema_name".to_owned()),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1344,7 +1383,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&None
|
&None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1360,7 +1400,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&Some("public".to_owned())
|
&Some("public".to_owned()),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1376,7 +1417,8 @@ mod tests {
|
|||||||
entity,
|
entity,
|
||||||
&crate::WithSerde::None,
|
&crate::WithSerde::None,
|
||||||
&crate::DateTimeCrate::Chrono,
|
&crate::DateTimeCrate::Chrono,
|
||||||
&Some("schema_name".to_owned())
|
&Some("schema_name".to_owned()),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1481,11 +1523,13 @@ mod tests {
|
|||||||
cake_entity: &Entity,
|
cake_entity: &Entity,
|
||||||
entity_serde_variant: &(String, WithSerde, Option<String>),
|
entity_serde_variant: &(String, WithSerde, Option<String>),
|
||||||
generator: Box<
|
generator: Box<
|
||||||
dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option<String>) -> Vec<TokenStream>,
|
dyn Fn(&Entity, &WithSerde, &DateTimeCrate, &Option<String>, bool) -> Vec<TokenStream>,
|
||||||
>,
|
>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
|
let mut reader = BufReader::new(entity_serde_variant.0.as_bytes());
|
||||||
let mut lines: Vec<String> = Vec::new();
|
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())?;
|
reader.read_until(b'\n', &mut Vec::new())?;
|
||||||
|
|
||||||
@ -1496,11 +1540,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let content = lines.join("");
|
let content = lines.join("");
|
||||||
let expected: TokenStream = content.parse().unwrap();
|
let expected: TokenStream = content.parse().unwrap();
|
||||||
|
println!("{:?}", entity_serde_variant.1);
|
||||||
let generated = generator(
|
let generated = generator(
|
||||||
cake_entity,
|
cake_entity,
|
||||||
&entity_serde_variant.1,
|
&entity_serde_variant.1,
|
||||||
&DateTimeCrate::Chrono,
|
&DateTimeCrate::Chrono,
|
||||||
&entity_serde_variant.2,
|
&entity_serde_variant.2,
|
||||||
|
serde_skip_deserializing_primary_key,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(TokenStream::new(), |mut acc, tok| {
|
.fold(TokenStream::new(), |mut acc, tok| {
|
||||||
|
@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[sea_orm(table_name = "cake")]
|
#[sea_orm(table_name = "cake")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
pub name: Option<String> ,
|
pub name: Option<String> ,
|
||||||
|
@ -7,6 +7,7 @@ use serde::Deserialize;
|
|||||||
#[sea_orm(table_name = "cake")]
|
#[sea_orm(table_name = "cake")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
pub name: Option<String> ,
|
pub name: Option<String> ,
|
||||||
|
@ -14,6 +14,7 @@ impl EntityName for Entity {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: Option<String> ,
|
pub name: Option<String> ,
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ impl EntityName for Entity {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Deserialize)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: Option<String> ,
|
pub name: Option<String> ,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user