From a8ae8eff5546f2f20096b0587ea645b8e1150248 Mon Sep 17 00:00:00 2001 From: Manuel Gugger Date: Sun, 31 Jul 2022 16:33:02 +0200 Subject: [PATCH] add search with 'contains' --- .../actix_admin_macros/src/attributes.rs | 3 +- actix_admin/actix_admin_macros/src/lib.rs | 14 ++++++--- .../actix_admin_macros/src/model_fields.rs | 3 +- .../actix_admin_macros/src/struct_fields.rs | 26 ++++++++++++++-- actix_admin/src/model.rs | 1 + actix_admin/src/routes/list.rs | 3 +- actix_admin/src/view_model.rs | 1 + actix_admin/templates/base.html | 5 ++-- actix_admin/templates/create_or_edit.html | 28 ++++++++++-------- actix_admin/templates/list.html | 14 ++++----- actix_admin/templates/spinner.svg | 7 +++++ database.db-wal | Bin 185432 -> 197792 bytes src/entity/post.rs | 2 ++ 13 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 actix_admin/templates/spinner.svg diff --git a/actix_admin/actix_admin_macros/src/attributes.rs b/actix_admin/actix_admin_macros/src/attributes.rs index 4b405a2..8418a34 100644 --- a/actix_admin/actix_admin_macros/src/attributes.rs +++ b/actix_admin/actix_admin_macros/src/attributes.rs @@ -12,7 +12,8 @@ pub mod derive_attr { pub struct ActixAdmin { pub primary_key: Option<()>, pub html_input_type: Option, - pub select_list: Option + pub select_list: Option, + pub searchable: Option<()> //pub inner_type: Option, // Anything that implements `syn::parse::Parse` is supported. diff --git a/actix_admin/actix_admin_macros/src/lib.rs b/actix_admin/actix_admin_macros/src/lib.rs index d593511..f02687b 100644 --- a/actix_admin/actix_admin_macros/src/lib.rs +++ b/actix_admin/actix_admin_macros/src/lib.rs @@ -13,7 +13,8 @@ use struct_fields::{ get_primary_key_field_name, get_actix_admin_fields_select_list, get_actix_admin_fields_is_option_list, - get_fields_for_validate_model + get_fields_for_validate_model, + get_actix_admin_fields_searchable }; mod selectlist_fields; @@ -44,6 +45,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea let fields_for_from_model = get_fields_for_from_model(&fields); let field_for_primary_key = get_field_for_primary_key(&fields); let fields_for_validate_model = get_fields_for_validate_model(&fields); + let fields_searchable = get_actix_admin_fields_searchable(&fields); let select_lists = get_select_lists(&fields); @@ -96,8 +98,8 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #[async_trait(?Send)] impl ActixAdminViewModelTrait for Entity { - async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize) -> (usize, Vec) { - let entities = Entity::list_model(db, page, entities_per_page).await; + async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize, search: &String) -> (usize, Vec) { + let entities = Entity::list_model(db, page, entities_per_page, search).await; entities } @@ -163,9 +165,13 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #[async_trait] impl ActixAdminModelTrait for Entity { - async fn list_model(db: &DatabaseConnection, page: usize, posts_per_page: usize) -> (usize, Vec) { + async fn list_model(db: &DatabaseConnection, page: usize, posts_per_page: usize, search: &String) -> (usize, Vec) { use sea_orm::{ query::* }; let paginator = Entity::find() + .filter( + Condition::any() + #(#fields_searchable)* + ) .order_by_asc(Column::Id) .paginate(db, posts_per_page); let num_pages = paginator.num_pages().await.ok().unwrap(); diff --git a/actix_admin/actix_admin_macros/src/model_fields.rs b/actix_admin/actix_admin_macros/src/model_fields.rs index 49eeef0..d05db0b 100644 --- a/actix_admin/actix_admin_macros/src/model_fields.rs +++ b/actix_admin/actix_admin_macros/src/model_fields.rs @@ -11,7 +11,8 @@ pub struct ModelField { pub inner_type: Option, pub primary_key: bool, pub html_input_type: String, - pub select_list: String + pub select_list: String, + pub searchable: bool } impl ModelField { diff --git a/actix_admin/actix_admin_macros/src/struct_fields.rs b/actix_admin/actix_admin_macros/src/struct_fields.rs index 9e171ec..e8e7a80 100644 --- a/actix_admin/actix_admin_macros/src/struct_fields.rs +++ b/actix_admin/actix_admin_macros/src/struct_fields.rs @@ -1,8 +1,8 @@ use crate::attributes::derive_attr; use crate::model_fields::ModelField; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{DeriveInput, Fields, LitStr}; +use syn::{DeriveInput, Fields, LitStr, Ident}; pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::Vec { let ast: DeriveInput = syn::parse(input).unwrap(); @@ -16,6 +16,10 @@ pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::V fields } +fn capitalize_first_letter(s: &str) -> String { + s[0..1].to_uppercase() + &s[1..] +} + pub fn filter_fields(fields: &Fields) -> Vec { fields .iter() @@ -31,6 +35,9 @@ pub fn filter_fields(fields: &Fields) -> Vec { let is_primary_key = actix_admin_attr .clone() .map_or(false, |attr| attr.primary_key.is_some()); + let is_searchable = actix_admin_attr + .clone() + .map_or(false, |attr| attr.searchable.is_some()); let select_list = actix_admin_attr.clone().map_or("".to_string(), |attr| { attr.select_list.map_or("".to_string(), |attr_field| { (LitStr::from(attr_field)).value() @@ -51,6 +58,7 @@ pub fn filter_fields(fields: &Fields) -> Vec { primary_key: is_primary_key, html_input_type: html_input_type, select_list: select_list, + searchable: is_searchable }; Some(model_field) } else { @@ -146,6 +154,20 @@ pub fn get_actix_admin_fields_html_input(fields: &Vec) -> Vec>() } +pub fn get_actix_admin_fields_searchable(fields: &Vec) -> Vec { + fields + .iter() + .filter(|model_field| model_field.searchable) + .map(|model_field| { + let column_name = format!("{}", capitalize_first_letter(&model_field.ident.to_string())); + let column_ident = Ident::new(&column_name, Span::call_site()); + quote! { + .add(Column::#column_ident.contains(&search)) + } + }) + .collect::>() +} + pub fn get_actix_admin_fields_select_list(fields: &Vec) -> Vec { fields .iter() diff --git a/actix_admin/src/model.rs b/actix_admin/src/model.rs index e00462c..bb554f9 100644 --- a/actix_admin/src/model.rs +++ b/actix_admin/src/model.rs @@ -10,6 +10,7 @@ pub trait ActixAdminModelTrait { db: &DatabaseConnection, page: usize, posts_per_page: usize, + search: &String ) -> (usize, Vec); fn get_fields() -> Vec; fn validate_model(model: &ActixAdminModel) -> HashMap; diff --git a/actix_admin/src/routes/list.rs b/actix_admin/src/routes/list.rs index e5c8472..b1d12f7 100644 --- a/actix_admin/src/routes/list.rs +++ b/actix_admin/src/routes/list.rs @@ -35,9 +35,10 @@ pub async fn list( .entities_per_page .unwrap_or(DEFAULT_ENTITIES_PER_PAGE); let render_partial = params.render_partial.unwrap_or(false); + let search = params.search.clone().unwrap_or(String::new()); let db = data.get_db(); - let result: (usize, Vec) = E::list(db, page, entities_per_page).await; + let result: (usize, Vec) = E::list(db, page, entities_per_page, &search).await; let entities = result.1; let num_pages = result.0; diff --git a/actix_admin/src/view_model.rs b/actix_admin/src/view_model.rs index 0e7ebaf..8694057 100644 --- a/actix_admin/src/view_model.rs +++ b/actix_admin/src/view_model.rs @@ -10,6 +10,7 @@ pub trait ActixAdminViewModelTrait { db: &DatabaseConnection, page: usize, entities_per_page: usize, + search: &String ) -> (usize, Vec); // TODO: Replace return value with proper Result Type containing Ok or Err diff --git a/actix_admin/templates/base.html b/actix_admin/templates/base.html index e616b90..439734d 100644 --- a/actix_admin/templates/base.html +++ b/actix_admin/templates/base.html @@ -5,7 +5,7 @@ {% else %} - + @@ -15,11 +15,12 @@ -
+
{% include "header.html" %}
{% block content %} diff --git a/actix_admin/templates/create_or_edit.html b/actix_admin/templates/create_or_edit.html index 53401c9..24b16ed 100644 --- a/actix_admin/templates/create_or_edit.html +++ b/actix_admin/templates/create_or_edit.html @@ -8,8 +8,8 @@ {% else %} + value="{{ model.values | get(key=model_field.field_name, default="") | split(pat=" _") | join(sep=" " ) + | title }}" name="{{ model_field.field_name }}" placeholder="{{ model_field.field_name }}" + aria-label="{{ model_field.field_name }}" {% if model.errors | get(key=model_field.field_name, + default="" ) !="" %} placeholder="Invalid" aria-invalid="true" {% endif %}> {% endif %} {%- endfor %} - - Cancel +
+
+ +
+
+ Cancel +
+
diff --git a/actix_admin/templates/list.html b/actix_admin/templates/list.html index 7502094..9657bbb 100644 --- a/actix_admin/templates/list.html +++ b/actix_admin/templates/list.html @@ -5,24 +5,22 @@ {% if not render_partial or render_partial == false %}
-
- Create -
- +
@@ -32,7 +30,7 @@ - +
{% include "spinner.svg" %}
{{ view_model.primary_key | title }} {% for model_field in view_model.fields -%} diff --git a/actix_admin/templates/spinner.svg b/actix_admin/templates/spinner.svg new file mode 100644 index 0000000..e6bc301 --- /dev/null +++ b/actix_admin/templates/spinner.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/database.db-wal b/database.db-wal index b2d8dc4668c8fb91bc88e01e97bb3ffff0143d35..91856a6aeddcfe1af4fba5a4aefa8edc545e3199 100644 GIT binary patch delta 271 zcmcbyf_p(HPeTji7N&?RlOHq)h$Xa5?9aWnhJ}}ZB?A+GHv@k+|9bxQ+x@mM1@N-~ z#dN3dy38~WtW+{J}6W6s&>jG6DCMT9;=I6PVr507DBvyu`CgQLb E0FNzHl>h($ delta 13 UcmZ3`$#Y``cS8%~7N&?R04T%-r~m)} diff --git a/src/entity/post.rs b/src/entity/post.rs index fe35628..d7c555f 100644 --- a/src/entity/post.rs +++ b/src/entity/post.rs @@ -12,8 +12,10 @@ pub struct Model { #[serde(skip_deserializing)] #[actix_admin(primary_key)] pub id: i32, + #[actix_admin(searchable)] pub title: String, #[sea_orm(column_type = "Text")] + #[actix_admin(searchable)] pub text: String, #[actix_admin(select_list="Tea")] pub tea_mandatory: Tea,