From 223ad29266338e1114c530f06fcd412d28b5056c Mon Sep 17 00:00:00 2001 From: Manuel Gugger Date: Thu, 15 Jun 2023 11:33:08 +0200 Subject: [PATCH] update sea-orm and fix empty list when no searchable conditions --- Cargo.toml | 20 ++++----- actix_admin_macros/src/lib.rs | 82 ++++++++++++++++++++++------------- docs/content/_index.md | 4 +- examples/basic/entity/mod.rs | 31 +++++++++++-- examples/basic/entity/user.rs | 20 +++++++++ examples/basic/main.rs | 7 +-- src/templates/head.html | 10 ++++- src/templates/list.html | 70 +++++++++--------------------- 8 files changed, 145 insertions(+), 99 deletions(-) create mode 100644 examples/basic/entity/user.rs diff --git a/Cargo.toml b/Cargo.toml index 686253e..5dcb7e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,23 +23,23 @@ actix-web = "^4.3.1" actix-session = { version = "^0.7.2", features = [] } actix-multipart = "^0.6.0" actix-files = "^0.6.2" -futures-util = "0.3.27" -chrono = "0.4.24" -tera = "^1.18.1" -async-trait = "^0.1.67" +futures-util = "0.3.28" +chrono = "0.4.26" +tera = "^1.19.0" +async-trait = "^0.1.68" lazy_static = "^1.4.0" itertools = "^0.10.5" -serde = "^1.0.158" -serde_derive = "^1.0.158" -sea-orm = { version = "^0.10.6", features = [], default-features = false } +serde = "^1.0.164" +serde_derive = "^1.0.164" +sea-orm = { version = "^0.11.3", features = [], default-features = false } actix-admin-macros = { version = "0.4.0", path = "actix_admin_macros" } derive_more = "0.99.17" -regex = "1.7.1" +regex = "1.8.4" [dev-dependencies] -sea-orm = { version = "^0.10.6", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true } +sea-orm = { version = "^0.11.3", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true } actix-rt = "2.8.0" azure_auth = { path = "./examples/azure_auth/azure_auth" } -oauth2 = "4.3" +oauth2 = "4.4.1" dotenv = "0.15" actix-session = { version = "0.7.2", features = ["cookie-session"] } \ No newline at end of file diff --git a/actix_admin_macros/src/lib.rs b/actix_admin_macros/src/lib.rs index 09ec589..88224d1 100644 --- a/actix_admin_macros/src/lib.rs +++ b/actix_admin_macros/src/lib.rs @@ -15,12 +15,16 @@ mod attributes; mod model_fields; #[proc_macro_derive(DeriveActixAdminEnumSelectList, attributes(actix_admin))] -pub fn derive_actix_admin_enum_select_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_actix_admin_enum_select_list( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { get_select_list_from_enum(input) } #[proc_macro_derive(DeriveActixAdminModelSelectList, attributes(actix_admin))] -pub fn derive_actix_admin_model_select_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_actix_admin_model_select_list( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { get_select_list_from_model(input) } @@ -30,10 +34,10 @@ pub fn derive_actix_admin(_input: proc_macro::TokenStream) -> proc_macro::TokenS use std::convert::From; use actix_admin::prelude::*; use sea_orm::{ - ActiveValue::Set, - ConnectOptions, - DatabaseConnection, - entity::*, + ActiveValue::Set, + ConnectOptions, + DatabaseConnection, + entity::*, query::*, EntityTrait }; @@ -82,7 +86,7 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac let custom_errors = Entity::validate(&active_model); model.custom_errors = custom_errors; } - } + } async fn create_entity(db: &DatabaseConnection, mut model: ActixAdminModel) -> Result { let new_model = ActiveModel::from(model.clone()); @@ -108,8 +112,8 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac Some(e) => { let mut entity: ActiveModel = e.into(); #(#fields_for_edit_model);*; - let entity: Model = entity.update(db).await?; - Ok(model) + let entity: Model = entity.update(db).await?; + Ok(model) }, _ => Err(ActixAdminError::EntityDoesNotExistError) } @@ -143,28 +147,46 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let fields = get_fields_for_tokenstream(input); - let field_names = get_fields_as_tokenstream(&fields, |model_field| -> String { model_field.ident.to_string() }); - let field_html_input_type = get_fields_as_tokenstream(&fields, |model_field| -> String { model_field.html_input_type.to_string() }); - let field_list_regex_mask = get_fields_as_tokenstream(&fields, |model_field| -> String { model_field.list_regex_mask.to_string() }); - let field_select_list = get_fields_as_tokenstream(&fields, |model_field| -> String { model_field.select_list.to_string() }); - let is_option_list = get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.is_option() }); + let field_names = get_fields_as_tokenstream(&fields, |model_field| -> String { + model_field.ident.to_string() + }); + let field_html_input_type = get_fields_as_tokenstream(&fields, |model_field| -> String { + model_field.html_input_type.to_string() + }); + let field_list_regex_mask = get_fields_as_tokenstream(&fields, |model_field| -> String { + model_field.list_regex_mask.to_string() + }); + let field_select_list = get_fields_as_tokenstream(&fields, |model_field| -> String { + model_field.select_list.to_string() + }); + let is_option_list = + get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.is_option() }); let fields_for_create_model = get_fields_for_create_model(&fields); 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_type_path = get_fields_as_tokenstream(&fields, |model_field| -> String { model_field.get_type_path_string() }); - let fields_textarea = get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.textarea }); - let fields_file_upload = get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.file_upload }); + let fields_type_path = get_fields_as_tokenstream(&fields, |model_field| -> String { + model_field.get_type_path_string() + }); + let fields_textarea = + get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.textarea }); + let fields_file_upload = + get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.file_upload }); let fields_match_name_to_columns = get_match_name_to_column(&fields); - let fields_list_sort_positions = get_fields_as_tokenstream(&fields, |model_field| -> usize { model_field.list_sort_position }); - let fields_list_hide_column = get_fields_as_tokenstream(&fields, |model_field| -> bool { model_field.list_hide_column }); + let fields_list_sort_positions = get_fields_as_tokenstream(&fields, |model_field| -> usize { + model_field.list_sort_position + }); + let fields_list_hide_column = get_fields_as_tokenstream(&fields, |model_field| -> bool { + model_field.list_hide_column + }); let fields_searchable = get_actix_admin_fields_searchable(&fields); + let has_searchable_fields = fields_searchable.len() > 0; let expanded = quote! { actix_admin::prelude::lazy_static! { pub static ref ACTIX_ADMIN_VIEWMODEL_FIELDS: Vec = { let mut vec = Vec::new(); - + let field_names = stringify!( #(#field_names),* ).split(",") @@ -207,9 +229,9 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T let list_regex_masks = [ #(#field_list_regex_mask),* ]; - + for (field_name, html_input_type, select_list, is_option_list, fields_type_path, is_textarea, is_file_upload, list_sort_position, list_hide_column, list_regex_mask) in actix_admin::prelude::izip!(&field_names, &html_input_types, &field_select_lists, is_option_lists, fields_type_paths, fields_textareas, fields_fileupload, list_sort_positions, list_hide_columns, list_regex_masks) { - + let select_list = select_list.replace('"', "").replace(' ', "").to_string(); let field_name = field_name.replace('"', "").replace(' ', "").to_string(); let html_input_type = html_input_type.replace('"', "").replace(' ', "").to_string(); @@ -260,26 +282,28 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T #[actix_admin::prelude::async_trait] impl ActixAdminModelTrait for Entity { async fn list_model(db: &DatabaseConnection, page: u64, posts_per_page: u64, search: &str, sort_by: &str, sort_order: &SortOrder) -> Result<(u64, Vec), ActixAdminError> { - use sea_orm::{ query::* }; - let sort_column = match sort_by { #(#fields_match_name_to_columns)* _ => panic!("Unknown column") }; - - let query = if sort_order.eq(&SortOrder::Asc) { + + let mut query = if sort_order.eq(&SortOrder::Asc) { Entity::find().order_by_asc(sort_column) } else { Entity::find().order_by_desc(sort_column) }; - let paginator = query + if (#has_searchable_fields) { + query = query .filter( Condition::any() #(#fields_searchable)* ) - .paginate(db, posts_per_page); + } + + let paginator = query.paginate(db, posts_per_page); let num_pages = paginator.num_pages().await?; + let mut model_entities = Vec::new(); if (num_pages == 0) { return Ok((num_pages, model_entities)) }; let entities = paginator @@ -290,7 +314,7 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T ActixAdminModel::from(entity) ); } - + Ok((num_pages, model_entities)) } diff --git a/docs/content/_index.md b/docs/content/_index.md index 357ecc3..f9579c0 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -32,10 +32,10 @@ name = "actix-admin-example" path = "main.rs" [dependencies] -actix-web = "^4.2.1" +actix-web = "^4.3.1" actix-rt = "2.7.0" actix-multipart = "^0.4.0" -sea-orm = { version = "^0.11.1", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true } +sea-orm = { version = "^0.11.3", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true } chrono = "0.4.23" tera = "^1.17.1" serde = "^1.0.152" diff --git a/examples/basic/entity/mod.rs b/examples/basic/entity/mod.rs index ae6ea04..cec8803 100644 --- a/examples/basic/entity/mod.rs +++ b/examples/basic/entity/mod.rs @@ -1,10 +1,12 @@ // setup use sea_orm::sea_query::{ForeignKeyCreateStatement, ColumnDef, TableCreateStatement}; -use sea_orm::{Set, EntityTrait, error::*, sea_query, ConnectionTrait, DbConn, ExecResult}; +use sea_orm::{Set, EntityTrait, error::*, sea_query, ConnectionTrait, DbConn, ExecResult }; pub mod comment; pub mod post; +pub mod user; pub use comment::Entity as Comment; pub use post::Entity as Post; +pub use user::Entity as User; use chrono::{Local, Duration, DurationRound}; use sea_orm::prelude::Decimal; @@ -61,7 +63,22 @@ pub async fn create_post_table(db: &DbConn) -> Result { ) .to_owned(); - let res = create_table(db, &stmt).await; + let _res = create_table(db, &stmt).await; + + let stmt = sea_query::Table::create() + .table(user::Entity) + .if_not_exists() + .col( + ColumnDef::new(post::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(user::Column::Name).string().not_null()) + .to_owned(); + + let _res = create_table(db, &stmt).await; for i in 1..1000 { let row = post::ActiveModel { @@ -87,5 +104,13 @@ pub async fn create_post_table(db: &DbConn) -> Result { let _res = Comment::insert(row).exec(db).await; } - res + for i in 1..100 { + let row = user::ActiveModel { + name: Set(format!("user {}", i)), + ..Default::default() + }; + let _res = User::insert(row).exec(db).await; +} + + _res } diff --git a/examples/basic/entity/user.rs b/examples/basic/entity/user.rs new file mode 100644 index 0000000..19cce78 --- /dev/null +++ b/examples/basic/entity/user.rs @@ -0,0 +1,20 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; +use actix_admin::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)] +#[sea_orm(table_name = "user")] +pub struct Model { + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + #[actix_admin(primary_key)] + pub id: i32, + pub name: String +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActixAdminModelValidationTrait for Entity {} \ No newline at end of file diff --git a/examples/basic/main.rs b/examples/basic/main.rs index 1174f62..6599dca 100644 --- a/examples/basic/main.rs +++ b/examples/basic/main.rs @@ -5,7 +5,7 @@ use actix_web::{web, App, HttpServer, middleware}; use sea_orm::{ConnectOptions, DatabaseConnection}; use std::time::Duration; mod entity; -use entity::{Post, Comment}; +use entity::{Post, Comment, User}; #[derive(Clone)] pub struct AppState { @@ -33,13 +33,14 @@ fn create_actix_admin_builder() -> ActixAdminBuilder { let mut admin_builder = ActixAdminBuilder::new(configuration); - let post_view_model = ActixAdminViewModel::from(Post); admin_builder.add_entity::(&post_view_model); - let some_category = "Groupings"; + let some_category = "Group"; let comment_view_model = ActixAdminViewModel::from(Comment); admin_builder.add_entity_to_category::(&comment_view_model, some_category); + let user_view_model = ActixAdminViewModel::from(User); + admin_builder.add_entity_to_category::(&user_view_model, some_category); admin_builder } diff --git a/src/templates/head.html b/src/templates/head.html index 9de5315..538d487 100644 --- a/src/templates/head.html +++ b/src/templates/head.html @@ -39,8 +39,14 @@ } } - function sort_by(column, order) { - document.getElementsByName("sort_order").forEach((e) => e.value = order); + function sort_by(column) { + console.log(column); + current_sort_order = document.getElementsByName("sort_order")[0].value; + if (current_sort_order == "Asc") { + document.getElementsByName("sort_order").forEach((e) => e.value = "Desc"); + } else { + document.getElementsByName("sort_order").forEach((e) => e.value = "Asc"); + } document.getElementsByName("sort_by").forEach((e) => e.value = column); document.getElementById('table_form').requestSubmit(); } diff --git a/src/templates/list.html b/src/templates/list.html index 691e429..c0c5a57 100644 --- a/src/templates/list.html +++ b/src/templates/list.html @@ -71,63 +71,33 @@ - +
- {% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) | sort(attribute="list_sort_position") -%} - {%- endfor %} {% endif %} {%- endfor %} -
- + {{ + view_model.primary_key | title }} + {% if sort_by == view_model.primary_key %} + {% if sort_order == "Asc" %} + + {% elif sort_order == "Desc" %} + + {% endif %} + {% endif %} - + {{ + model_field.field_name | split(pat="_") | join(sep=" ") | title }} + {% if sort_by == model_field.field_name %} + {% if sort_order == "Asc" %} + + {% elif sort_order == "Desc" %} + + {% endif %} + {% endif %} @@ -164,7 +134,7 @@ {{ entity.values | get(key=model_field.field_name) }} +