Repository: SeaQL/sea-orm (rust)

Issue:
`EntityModel` to ignore a column
with the attr `#[sea_orm(ignore)]`

Pull request:
Model ignore attributes
Resolve #159 

Patch:
diff --git a/examples/tokio/Cargo.toml b/examples/tokio/Cargo.toml
index b1124aad..173e9906 100644
--- a/examples/tokio/Cargo.toml
+++ b/examples/tokio/Cargo.toml
@@ -10,5 +10,3 @@ publish = false
 [dependencies]
 sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-tokio-native-tls" ] }
 tokio = { version = "1", features = ["full"] }
-env_logger = { version = "^0.9" }
-log = { version = "^0.4" }
diff --git a/examples/tokio/src/main.rs b/examples/tokio/src/main.rs
index 14a76086..dd81b4ab 100644
--- a/examples/tokio/src/main.rs
+++ b/examples/tokio/src/main.rs
@@ -3,12 +3,7 @@ use sea_orm::*;
 
 #[tokio::main]
 pub async fn main() {
-    env_logger::builder()
-        .filter_level(log::LevelFilter::Debug)
-        .is_test(true)
-        .init();
-
-    let db = Database::connect("mysql://sea:sea@localhost/bakery")
+    let db = Database::connect("sql://sea:sea@localhost/bakery")
         .await
         .unwrap();
 
diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml
index 484b6efb..4c23912c 100644
--- a/sea-orm-cli/Cargo.toml
+++ b/sea-orm-cli/Cargo.toml
@@ -22,16 +22,13 @@ clap = { version = "^2.33.3" }
 dotenv = { version = "^0.15" }
 async-std = { version = "^1.9", features = [ "attributes" ] }
 sea-orm-codegen = { version = "^0.2.0", path = "../sea-orm-codegen" }
-sea-schema = { version = "^0.2.8", git = "https://github.com/SeaQL/sea-schema.git", default-features = false, features = [
-    "debug-print",
+sea-schema = { version = "^0.2.7", git = "https://github.com/SeaQL/sea-schema.git", default-features = false, features = [
     "sqlx-mysql",
     "sqlx-postgres",
     "discovery",
     "writer",
 ] }
 sqlx = { version = "^0.5", default-features = false, features = [ "mysql", "postgres" ] }
-env_logger = { version = "^0.9" }
-log = { version = "^0.4" }
 
 [features]
 default = [ "runtime-async-std-native-tls" ]
diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs
index 100c747c..400374c5 100644
--- a/sea-orm-cli/src/cli.rs
+++ b/sea-orm-cli/src/cli.rs
@@ -62,13 +62,5 @@ pub fn build_cli() -> App<'static, 'static> {
         .version(env!("CARGO_PKG_VERSION"))
         .setting(AppSettings::VersionlessSubcommands)
         .subcommand(entity_subcommand)
-        .arg(
-            Arg::with_name("VERBOSE")
-                .long("verbose")
-                .short("v")
-                .help("Show debug messages")
-                .takes_value(false)
-                .global(true),
-        )
         .setting(AppSettings::SubcommandRequiredElseHelp)
 }
diff --git a/sea-orm-cli/src/main.rs b/sea-orm-cli/src/main.rs
index e7041ef0..2f51aefb 100644
--- a/sea-orm-cli/src/main.rs
+++ b/sea-orm-cli/src/main.rs
@@ -1,6 +1,5 @@
 use clap::ArgMatches;
 use dotenv::dotenv;
-use log::LevelFilter;
 use sea_orm_codegen::{EntityTransformer, OutputFile};
 use std::{error::Error, fmt::Display, fs, io::Write, path::Path, process::Command};
 
@@ -34,12 +33,6 @@ async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Er
                     !table.starts_with("_")
                 }
             };
-            if args.is_present("VERBOSE") {
-                let _ = ::env_logger::builder()
-                    .filter_level(LevelFilter::Debug)
-                    .is_test(true)
-                    .try_init();
-            }
 
             let table_stmts = if url.starts_with("mysql://") {
                 use sea_schema::mysql::discovery::SchemaDiscovery;
diff --git a/sea-orm-macros/src/derives/active_model.rs b/sea-orm-macros/src/derives/active_model.rs
index 16e09f2a..3a96860e 100644
--- a/sea-orm-macros/src/derives/active_model.rs
+++ b/sea-orm-macros/src/derives/active_model.rs
@@ -1,3 +1,4 @@
+use crate::util::field_not_ignored;
 use heck::CamelCase;
 use proc_macro2::{Ident, TokenStream};
 use quote::{format_ident, quote, quote_spanned};
@@ -14,7 +15,9 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
                 ident.span() => compile_error!("you can only derive DeriveActiveModel on structs");
             })
         }
-    };
+    }
+    .into_iter()
+    .filter(field_not_ignored);
 
     let field: Vec<Ident> = fields
         .clone()
diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs
index 66c5725f..768d3b7c 100644
--- a/sea-orm-macros/src/derives/entity_model.rs
+++ b/sea-orm-macros/src/derives/entity_model.rs
@@ -68,6 +68,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
                     let mut default_value = None;
                     let mut default_expr = None;
                     let mut indexed = false;
+                    let mut ignore = false;
                     let mut unique = false;
                     let mut sql_type = None;
                     // search for #[sea_orm(primary_key, auto_increment = false, column_type = "String(Some(255))", default_value = "new user", default_expr = "gen_random_uuid()", nullable, indexed, unique)]
@@ -120,7 +121,10 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
                                     }
                                     Meta::Path(p) => {
                                         if let Some(name) = p.get_ident() {
-                                            if name == "primary_key" {
+                                            if name == "ignore" {
+                                                ignore = true;
+                                                break;
+                                            } else if name == "primary_key" {
                                                 primary_keys.push(quote! { #field_name });
                                                 primary_key_types.push(field.ty.clone());
                                             } else if name == "nullable" {
@@ -138,6 +142,11 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
                         }
                     }
 
+                    if ignore {
+                        columns_enum.pop();
+                        continue;
+                    }
+
                     let field_type = match sql_type {
                         Some(t) => t,
                         None => {
diff --git a/sea-orm-macros/src/derives/model.rs b/sea-orm-macros/src/derives/model.rs
index 669dbe9e..9d619991 100644
--- a/sea-orm-macros/src/derives/model.rs
+++ b/sea-orm-macros/src/derives/model.rs
@@ -1,8 +1,9 @@
-use crate::attributes::derive_attr;
+use crate::{attributes::derive_attr, util::field_not_ignored};
 use heck::CamelCase;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, quote_spanned};
 use std::iter::FromIterator;
+use syn::Ident;
 
 enum Error {
     InputNotStruct,
@@ -14,6 +15,7 @@ struct DeriveModel {
     entity_ident: syn::Ident,
     field_idents: Vec<syn::Ident>,
     ident: syn::Ident,
+    ignore_attrs: Vec<bool>,
 }
 
 impl DeriveModel {
@@ -48,11 +50,17 @@ impl DeriveModel {
             })
             .collect();
 
+        let ignore_attrs = fields
+            .iter()
+            .map(|field| !field_not_ignored(field))
+            .collect();
+
         Ok(DeriveModel {
             column_idents,
             entity_ident,
             field_idents,
             ident,
+            ignore_attrs,
         })
     }
 
@@ -70,23 +78,56 @@ impl DeriveModel {
         let ident = &self.ident;
         let field_idents = &self.field_idents;
         let column_idents = &self.column_idents;
+        let field_values: Vec<TokenStream> = column_idents
+            .iter()
+            .zip(&self.ignore_attrs)
+            .map(|(column_ident, ignore)| {
+                if *ignore {
+                    quote! {
+                        Default::default()
+                    }
+                } else {
+                    quote! {
+                        row.try_get(pre, sea_orm::IdenStatic::as_str(&<<Self as sea_orm::ModelTrait>::Entity as sea_orm::entity::EntityTrait>::Column::#column_ident).into())?
+                    }
+                }
+            })
+            .collect();
 
         quote!(
             impl sea_orm::FromQueryResult for #ident {
                 fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> Result<Self, sea_orm::DbErr> {
                     Ok(Self {
-                        #(#field_idents: row.try_get(pre, sea_orm::IdenStatic::as_str(&<<Self as sea_orm::ModelTrait>::Entity as sea_orm::entity::EntityTrait>::Column::#column_idents).into())?),*
+                        #(#field_idents: #field_values),*
                     })
                 }
             }
         )
     }
 
-    fn impl_model_trait(&self) -> TokenStream {
+    fn impl_model_trait<'a>(&'a self) -> TokenStream {
         let ident = &self.ident;
         let entity_ident = &self.entity_ident;
-        let field_idents = &self.field_idents;
-        let column_idents = &self.column_idents;
+        let ignore_attrs = &self.ignore_attrs;
+        let ignore = |(ident, ignore): (&'a Ident, &bool)| -> Option<&'a Ident> {
+            if *ignore {
+                None
+            } else {
+                Some(ident)
+            }
+        };
+        let field_idents: Vec<&Ident> = self
+            .field_idents
+            .iter()
+            .zip(ignore_attrs)
+            .filter_map(ignore)
+            .collect();
+        let column_idents: Vec<&Ident> = self
+            .column_idents
+            .iter()
+            .zip(ignore_attrs)
+            .filter_map(ignore)
+            .collect();
 
         let missing_field_msg = format!("field does not exist on {}", ident);
 
diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs
index 879ad5eb..629c5c18 100644
--- a/sea-orm-macros/src/lib.rs
+++ b/sea-orm-macros/src/lib.rs
@@ -5,6 +5,7 @@ use syn::{parse_macro_input, DeriveInput, Error};
 
 mod attributes;
 mod derives;
+mod util;
 
 #[proc_macro_derive(DeriveEntity, attributes(sea_orm))]
 pub fn derive_entity(input: TokenStream) -> TokenStream {
@@ -73,7 +74,7 @@ pub fn derive_model(input: TokenStream) -> TokenStream {
         .into()
 }
 
-#[proc_macro_derive(DeriveActiveModel)]
+#[proc_macro_derive(DeriveActiveModel, attributes(sea_orm))]
 pub fn derive_active_model(input: TokenStream) -> TokenStream {
     let DeriveInput { ident, data, .. } = parse_macro_input!(input);
 
diff --git a/sea-orm-macros/src/util.rs b/sea-orm-macros/src/util.rs
new file mode 100644
index 00000000..7dda1087
--- /dev/null
+++ b/sea-orm-macros/src/util.rs
@@ -0,0 +1,26 @@
+use syn::{punctuated::Punctuated, token::Comma, Field, Meta};
+
+pub(crate) fn field_not_ignored(field: &Field) -> bool {
+    for attr in field.attrs.iter() {
+        if let Some(ident) = attr.path.get_ident() {
+            if ident != "sea_orm" {
+                continue;
+            }
+        } else {
+            continue;
+        }
+
+        if let Ok(list) = attr.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated) {
+            for meta in list.iter() {
+                if let Meta::Path(path) = meta {
+                    if let Some(name) = path.get_ident() {
+                        if name == "ignore" {
+                            return false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    true
+}
diff --git a/src/entity/model.rs b/src/entity/model.rs
index c6129ad7..25a43753 100644
--- a/src/entity/model.rs
+++ b/src/entity/model.rs
@@ -1,7 +1,4 @@
-use crate::{
-    DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Select, SelectModel,
-    SelectorRaw, Statement,
-};
+use crate::{DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Select};
 pub use sea_query::Value;
 use std::fmt::Debug;
 
@@ -28,75 +25,15 @@ pub trait ModelTrait: Clone + Send + Debug {
     }
 }
 
-pub trait FromQueryResult: Sized {
-    fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>;
+pub trait FromQueryResult {
+    fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>
+    where
+        Self: Sized;
 
-    fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
+    fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr>
+    where
+        Self: Sized,
+    {
         Ok(Self::from_query_result(res, pre).ok())
     }
-
-    /// ```
-    /// # #[cfg(feature = "mock")]
-    /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};
-    /// #
-    /// # let db = MockDatabase::new(DbBackend::Postgres)
-    /// #     .append_query_results(vec![vec![
-    /// #         maplit::btreemap! {
-    /// #             "name" => Into::<Value>::into("Chocolate Forest"),
-    /// #             "num_of_cakes" => Into::<Value>::into(1),
-    /// #         },
-    /// #         maplit::btreemap! {
-    /// #             "name" => Into::<Value>::into("New York Cheese"),
-    /// #             "num_of_cakes" => Into::<Value>::into(1),
-    /// #         },
-    /// #     ]])
-    /// #     .into_connection();
-    /// #
-    /// use sea_orm::{entity::*, query::*, tests_cfg::cake, FromQueryResult};
-    ///
-    /// #[derive(Debug, PartialEq, FromQueryResult)]
-    /// struct SelectResult {
-    ///     name: String,
-    ///     num_of_cakes: i32,
-    /// }
-    ///
-    /// # let _: Result<(), DbErr> = smol::block_on(async {
-    /// #
-    /// let res: Vec<SelectResult> = SelectResult::find_by_statement(Statement::from_sql_and_values(
-    ///     DbBackend::Postgres,
-    ///     r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
-    ///     vec![],
-    /// ))
-    /// .all(&db)
-    /// .await?;
-    ///
-    /// assert_eq!(
-    ///     res,
-    ///     vec![
-    ///         SelectResult {
-    ///             name: "Chocolate Forest".to_owned(),
-    ///             num_of_cakes: 1,
-    ///         },
-    ///         SelectResult {
-    ///             name: "New York Cheese".to_owned(),
-    ///             num_of_cakes: 1,
-    ///         },
-    ///     ]
-    /// );
-    /// #
-    /// # Ok(())
-    /// # });
-    ///
-    /// assert_eq!(
-    ///     db.into_transaction_log(),
-    ///     vec![Transaction::from_sql_and_values(
-    ///         DbBackend::Postgres,
-    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
-    ///         vec![]
-    ///     ),]
-    /// );
-    /// ```
-    fn find_by_statement(stmt: Statement) -> SelectorRaw<SelectModel<Self>> {
-        SelectorRaw::<SelectModel<Self>>::from_statement(stmt)
-    }
 }
diff --git a/src/executor/select.rs b/src/executor/select.rs
index 959091c0..ef30bb3d 100644
--- a/src/executor/select.rs
+++ b/src/executor/select.rs
@@ -263,18 +263,6 @@ impl<S> SelectorRaw<S>
 where
     S: SelectorTrait,
 {
-    /// Create `SelectorRaw` from Statment. Executing this `SelectorRaw` will
-    /// return a type `M` which implement `FromQueryResult`.
-    pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>>
-    where
-        M: FromQueryResult,
-    {
-        SelectorRaw {
-            stmt,
-            selector: SelectModel { model: PhantomData },
-        }
-    }
-
     /// ```
     /// # #[cfg(feature = "mock")]
     /// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, Transaction, DbBackend};


Test patch:
diff --git a/src/tests_cfg/cake_filling_price.rs b/src/tests_cfg/cake_filling_price.rs
index e820ae0f..12779d14 100644
--- a/src/tests_cfg/cake_filling_price.rs
+++ b/src/tests_cfg/cake_filling_price.rs
@@ -19,6 +19,8 @@ pub struct Model {
     pub cake_id: i32,
     pub filling_id: i32,
     pub price: Decimal,
+    #[sea_orm(ignore)]
+    pub ignored_attr: i32,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
diff --git a/src/tests_cfg/filling.rs b/src/tests_cfg/filling.rs
index 433a89b8..14f7a849 100644
--- a/src/tests_cfg/filling.rs
+++ b/src/tests_cfg/filling.rs
@@ -9,6 +9,8 @@ pub struct Entity;
 pub struct Model {
     pub id: i32,
     pub name: String,
+    #[sea_orm(ignore)]
+    pub ignored_attr: i32,
 }
 
 // If your column names are not in snake-case, derive `DeriveCustomColumn` here.