add edit routes

This commit is contained in:
Manuel Gugger 2022-05-31 23:13:44 +02:00
parent 7748878c26
commit b7588163ed
10 changed files with 134 additions and 20 deletions

View File

@ -30,6 +30,18 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
})
.collect::<Vec<_>>();
let fields_for_edit_model = fields
.iter()
// TODO: filter id attr based on struct attr or sea_orm primary_key attr
.filter(|(_vis, ident, _ty)| !ident.to_string().eq("id"))
.map(|(_vis, ident, ty)| {
let ident_name = ident.to_string();
quote! {
entity.#ident = Set(model.get_value::<#ty>(#ident_name).unwrap())
}
})
.collect::<Vec<_>>();
let fields_for_from_model = fields
.iter()
.map(|(_vis, ident, _ty)| {
@ -96,14 +108,37 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
model
}
async fn delete_entity(db: &DatabaseConnection, mut model: ActixAdminModel) -> ActixAdminModel {
async fn get_entity(db: &DatabaseConnection, id: i32) -> ActixAdminModel {
// TODO: separate primary key from other keys
let entity = Entity::find_by_id(model.get_value::<i32>("id").unwrap()).one(db).await.unwrap().unwrap();
let delete_operation = entity.delete(db).await;
let entity = Entity::find_by_id(id).one(db).await.unwrap().unwrap();
let model = ActixAdminModel::from(entity);
model
}
async fn edit_entity(db: &DatabaseConnection, id: i32, mut model: ActixAdminModel) -> ActixAdminModel {
// TODO: separate primary key from other keys
let entity: Option<Model> = Entity::find_by_id(id).one(db).await.unwrap();
let mut entity: ActiveModel = entity.unwrap().into();
#(#fields_for_edit_model);*;
let entity: Model = entity.update(db).await.unwrap();
model
}
async fn delete_entity(db: &DatabaseConnection, id: i32) -> bool {
// TODO: separate primary key from other keys
let entity = Entity::find_by_id(id).one(db).await.unwrap().unwrap();
let result = entity.delete(db).await;
match result {
Ok(_) => true,
Err(_) => false
}
}
fn get_entity_name() -> String {
Entity.table_name().to_string()
}

View File

@ -1,16 +1,9 @@
use actix_web::{web};
use actix_web::web;
use std::collections::HashMap;
use crate::prelude::*;
use crate::routes::list;
use crate::routes::create_get;
use crate::routes::create_post;
use crate::routes::delete_post;
use crate::routes::index;
use crate::routes::{create_get, create_post, delete_post, edit_get, edit_post, index, list};
pub struct ActixAdminBuilder {
pub scopes: Vec<actix_web::Scope>,
@ -19,7 +12,10 @@ pub struct ActixAdminBuilder {
pub trait ActixAdminBuilderTrait {
fn new() -> Self;
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(&mut self, view_model: &ActixAdminViewModel);
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
&mut self,
view_model: &ActixAdminViewModel,
);
fn get_scope<T: ActixAdminAppDataTrait + 'static>(self) -> actix_web::Scope;
fn get_actix_admin(&self) -> ActixAdmin;
}
@ -44,6 +40,8 @@ impl ActixAdminBuilderTrait for ActixAdminBuilder {
.route("/list", web::get().to(list::<T, E>))
.route("/create", web::get().to(create_get::<T, E>))
.route("/create", web::post().to(create_post::<T, E>))
.route("/edit/{id}", web::get().to(edit_get::<T, E>))
.route("/edit/{id}", web::post().to(edit_post::<T, E>))
.route("/delete/{id}", web::post().to(delete_post::<T, E>)),
);

View File

@ -7,13 +7,14 @@ pub async fn delete_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>
_req: HttpRequest,
data: web::Data<T>,
text: String,
id: web::Path<i32>
) -> Result<HttpResponse, Error> {
let db = &data.get_db();
let entity_name = E::get_entity_name();
let actix_admin = data.get_actix_admin();
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
let mut admin_model = ActixAdminModel::from(text);
admin_model = E::delete_entity(db, admin_model).await;
// TODO:handle any errors
let _result = E::delete_entity(db, id.into_inner()).await;
Ok(HttpResponse::Found()
.append_header((

View File

@ -0,0 +1,34 @@
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use tera::{Context};
use crate::prelude::*;
use crate::TERA;
pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
_body: web::Payload,
text: String,
id: web::Path<i32>
) -> Result<HttpResponse, Error> {
let db = &data.get_db();
let entity_name = E::get_entity_name();
let entity_names = &data.get_actix_admin().entity_names;
let actix_admin = data.get_actix_admin();
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
let model = E::get_entity(db, id.into_inner()).await;
let mut ctx = Context::new();
ctx.insert("entity_names", &entity_names);
ctx.insert("view_model", &view_model);
ctx.insert("model", &model);
let body = TERA
.render("edit.html", &ctx)
.map_err(|err| error::ErrorInternalServerError(err))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}

View File

@ -0,0 +1,25 @@
use actix_web::http::header;
use actix_web::{web, Error, HttpRequest, HttpResponse};
use crate::prelude::*;
pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
text: String,
id: web::Path<i32>
) -> Result<HttpResponse, Error> {
let db = &data.get_db();
let entity_name = E::get_entity_name();
let actix_admin = data.get_actix_admin();
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
let mut admin_model = ActixAdminModel::from(text);
admin_model = E::edit_entity(db, id.into_inner(), admin_model).await;
Ok(HttpResponse::Found()
.append_header((
header::LOCATION,
format!("/admin/{}/list", view_model.entity_name),
))
.finish())
}

View File

@ -11,3 +11,8 @@ pub use list::list;
mod delete_post;
pub use delete_post::delete_post;
mod edit_get;
mod edit_post;
pub use edit_get::edit_get;
pub use edit_post::edit_post;

View File

@ -13,8 +13,11 @@ pub trait ActixAdminViewModelTrait {
entities_per_page: usize,
) -> Vec<ActixAdminModel>;
// TODO: Replace return value with proper Result Type containing Ok or Err
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
async fn delete_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
async fn delete_entity(db: &DatabaseConnection, id: i32) -> bool;
async fn get_entity(db: &DatabaseConnection, id: i32) -> ActixAdminModel;
async fn edit_entity(db: &DatabaseConnection, id: i32, model: ActixAdminModel) -> ActixAdminModel;
fn get_entity_name() -> String;
}

View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<form method="post">
<div class="grid">
{% for key, value in model.values -%}
<input type="text" value="{{ value }}" name="{{ key }}" placeholder="{{ key }}" aria-label="{{ key }}"><!-- required="" -->
{%- endfor %}
<button type="submit">Save</button>
</div>
</form>
{% endblock content %}

View File

@ -7,6 +7,7 @@
{% for model_field in view_model.fields -%}
<th>{{ model_field[0] }}</th>
{%- endfor %}
<th><!-- Edit Action --></th>
<th><!-- Delete Action --></th>
</tr>
{% for entity in entities -%}
@ -14,10 +15,10 @@
{% for model_field in view_model.fields -%}
<td>{{ entity.values | get(key=model_field[0]) }}</td>
{%- endfor %}
<td><a href="edit/{{ entity.values | get(key=view_model.fields[0][0]) }}" role="button">Edit</a></td>
<td>
<form method="post" action="delete/{{ entity.values | get(key=view_model.fields[0][0]) }}">
<input type="hidden" id="{{ view_model.fields[0][0] }}" name="{{ view_model.fields[0][0] }}" value="{{ entity.values | get(key=view_model.fields[0][0]) }}">
<button type="submit" class="outline">Delete</button>
<button type="submit">Delete</button>
</form>
</td>
</tr>

Binary file not shown.