mirror of
https://github.com/actix/examples
synced 2025-06-27 01:27:43 +02:00
Updated basics/to-do to v4 using sqlx.
This commit is contained in:
@ -1,9 +1,9 @@
|
||||
use actix_files::NamedFile;
|
||||
use actix_session::Session;
|
||||
use actix_web::{
|
||||
dev, error, http, middleware::ErrorHandlerResponse, web, Error, HttpResponse, Result,
|
||||
};
|
||||
use actix_web::middleware::ErrorHandlerResponse;
|
||||
use actix_web::{dev, error, http, web, Error, HttpResponse, Result};
|
||||
use serde::Deserialize;
|
||||
use sqlx::postgres::PgPool;
|
||||
use tera::{Context, Tera};
|
||||
|
||||
use crate::{
|
||||
@ -12,11 +12,13 @@ use crate::{
|
||||
};
|
||||
|
||||
pub async fn index(
|
||||
pool: web::Data<db::PgPool>,
|
||||
pool: web::Data<PgPool>,
|
||||
tmpl: web::Data<Tera>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let tasks = web::block(move || db::get_all_tasks(&pool)).await?;
|
||||
let tasks = db::get_all_tasks(&pool)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("tasks", &tasks);
|
||||
@ -42,7 +44,7 @@ pub struct CreateForm {
|
||||
|
||||
pub async fn create(
|
||||
params: web::Form<CreateForm>,
|
||||
pool: web::Data<db::PgPool>,
|
||||
pool: web::Data<PgPool>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
if params.description.is_empty() {
|
||||
@ -52,8 +54,8 @@ pub async fn create(
|
||||
)?;
|
||||
Ok(redirect_to("/"))
|
||||
} else {
|
||||
web::block(move || db::create_task(params.into_inner().description, &pool))
|
||||
.await?
|
||||
db::create_task(params.into_inner().description, &pool)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
session::set_flash(&session, FlashMessage::success("Task successfully added"))?;
|
||||
Ok(redirect_to("/"))
|
||||
@ -71,7 +73,7 @@ pub struct UpdateForm {
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
db: web::Data<db::PgPool>,
|
||||
db: web::Data<PgPool>,
|
||||
params: web::Path<UpdateParams>,
|
||||
form: web::Form<UpdateForm>,
|
||||
session: Session,
|
||||
@ -87,22 +89,22 @@ pub async fn update(
|
||||
}
|
||||
|
||||
async fn toggle(
|
||||
pool: web::Data<db::PgPool>,
|
||||
pool: web::Data<PgPool>,
|
||||
params: web::Path<UpdateParams>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
web::block(move || db::toggle_task(params.id, &pool))
|
||||
.await?
|
||||
db::toggle_task(params.id, &pool)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
Ok(redirect_to("/"))
|
||||
}
|
||||
|
||||
async fn delete(
|
||||
pool: web::Data<db::PgPool>,
|
||||
pool: web::Data<PgPool>,
|
||||
params: web::Path<UpdateParams>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
web::block(move || db::delete_task(params.id, &pool))
|
||||
.await?
|
||||
db::delete_task(params.id, &pool)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
session::set_flash(&session, FlashMessage::success("Task was deleted."))?;
|
||||
Ok(redirect_to("/"))
|
||||
|
@ -1,43 +1,36 @@
|
||||
use std::ops::Deref as _;
|
||||
|
||||
use diesel::{
|
||||
pg::PgConnection,
|
||||
r2d2::{ConnectionManager, Pool, PoolError, PooledConnection},
|
||||
};
|
||||
use sqlx::postgres::{PgPool, PgPoolOptions};
|
||||
|
||||
use crate::model::{NewTask, Task};
|
||||
|
||||
pub type PgPool = Pool<ConnectionManager<PgConnection>>;
|
||||
type PgPooledConnection = PooledConnection<ConnectionManager<PgConnection>>;
|
||||
|
||||
pub fn init_pool(database_url: &str) -> Result<PgPool, PoolError> {
|
||||
let manager = ConnectionManager::<PgConnection>::new(database_url);
|
||||
Pool::builder().build(manager)
|
||||
pub async fn init_pool(database_url: &str) -> Result<PgPool, sqlx::Error> {
|
||||
PgPoolOptions::new()
|
||||
.connect_timeout(std::time::Duration::from_secs(2))
|
||||
.connect(database_url)
|
||||
.await
|
||||
}
|
||||
|
||||
fn get_conn(pool: &PgPool) -> Result<PgPooledConnection, &'static str> {
|
||||
pool.get().map_err(|_| "Can't get connection")
|
||||
pub async fn get_all_tasks(pool: &PgPool) -> Result<Vec<Task>, &'static str> {
|
||||
Task::all(pool).await.map_err(|_| "Error retrieving tasks")
|
||||
}
|
||||
|
||||
pub fn get_all_tasks(pool: &PgPool) -> Result<Vec<Task>, &'static str> {
|
||||
Task::all(get_conn(pool)?.deref()).map_err(|_| "Error retrieving tasks")
|
||||
}
|
||||
|
||||
pub fn create_task(todo: String, pool: &PgPool) -> Result<(), &'static str> {
|
||||
pub async fn create_task(todo: String, pool: &PgPool) -> Result<(), &'static str> {
|
||||
let new_task = NewTask { description: todo };
|
||||
Task::insert(new_task, get_conn(pool)?.deref())
|
||||
Task::insert(new_task, pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| "Error inserting task")
|
||||
}
|
||||
|
||||
pub fn toggle_task(id: i32, pool: &PgPool) -> Result<(), &'static str> {
|
||||
Task::toggle_with_id(id, get_conn(pool)?.deref())
|
||||
pub async fn toggle_task(id: i32, pool: &PgPool) -> Result<(), &'static str> {
|
||||
Task::toggle_with_id(id, pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| "Error toggling task completion")
|
||||
}
|
||||
|
||||
pub fn delete_task(id: i32, pool: &PgPool) -> Result<(), &'static str> {
|
||||
Task::delete_with_id(id, get_conn(pool)?.deref())
|
||||
pub async fn delete_task(id: i32, pool: &PgPool) -> Result<(), &'static str> {
|
||||
Task::delete_with_id(id, pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| "Error deleting task")
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
use std::{env, io};
|
||||
|
||||
use actix_files::Files;
|
||||
@ -16,7 +13,6 @@ use tera::Tera;
|
||||
mod api;
|
||||
mod db;
|
||||
mod model;
|
||||
mod schema;
|
||||
mod session;
|
||||
|
||||
static SESSION_SIGNING_KEY: &[u8] = &[0; 32];
|
||||
@ -28,9 +24,12 @@ async fn main() -> io::Result<()> {
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let pool = db::init_pool(&database_url).expect("Failed to create pool");
|
||||
println!("{}", database_url);
|
||||
let pool = db::init_pool(&database_url)
|
||||
.await
|
||||
.expect("Failed to create pool");
|
||||
|
||||
log::info!("starting HTTP serer at http://localhost:8080");
|
||||
log::info!("starting HTTP serer at http://localhost:8088");
|
||||
|
||||
HttpServer::new(move || {
|
||||
log::debug!("Constructing the App");
|
||||
@ -50,8 +49,8 @@ async fn main() -> io::Result<()> {
|
||||
.handler(http::StatusCode::NOT_FOUND, api::not_found);
|
||||
|
||||
App::new()
|
||||
.app_data(templates)
|
||||
.app_data(pool.clone())
|
||||
.app_data(web::Data::new(templates))
|
||||
.app_data(web::Data::new(pool.clone()))
|
||||
.wrap(Logger::default())
|
||||
.wrap(session_store)
|
||||
.wrap(error_handlers)
|
||||
|
@ -1,19 +1,11 @@
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use serde::Serialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::schema::{
|
||||
tasks,
|
||||
tasks::dsl::{completed as task_completed, tasks as all_tasks},
|
||||
};
|
||||
|
||||
#[derive(Debug, Insertable)]
|
||||
#[table_name = "tasks"]
|
||||
#[derive(Debug)]
|
||||
pub struct NewTask {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Queryable, Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Task {
|
||||
pub id: i32,
|
||||
pub description: String,
|
||||
@ -21,27 +13,62 @@ pub struct Task {
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn all(conn: &PgConnection) -> QueryResult<Vec<Task>> {
|
||||
all_tasks.order(tasks::id.desc()).load::<Task>(conn)
|
||||
pub async fn all(connection: &PgPool) -> Result<Vec<Task>, sqlx::Error> {
|
||||
let tasks = sqlx::query_as!(
|
||||
Task,
|
||||
r#"
|
||||
SELECT *
|
||||
FROM tasks
|
||||
"#
|
||||
)
|
||||
.fetch_all(connection)
|
||||
.await?;
|
||||
Ok(tasks)
|
||||
}
|
||||
|
||||
pub fn insert(todo: NewTask, conn: &PgConnection) -> QueryResult<usize> {
|
||||
diesel::insert_into(tasks::table)
|
||||
.values(&todo)
|
||||
.execute(conn)
|
||||
pub async fn insert(todo: NewTask, connection: &PgPool) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO tasks (description)
|
||||
VALUES ($1)
|
||||
"#,
|
||||
todo.description,
|
||||
)
|
||||
.execute(connection)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn toggle_with_id(id: i32, conn: &PgConnection) -> QueryResult<usize> {
|
||||
let task = all_tasks.find(id).get_result::<Task>(conn)?;
|
||||
|
||||
let new_status = !task.completed;
|
||||
let updated_task = diesel::update(all_tasks.find(id));
|
||||
updated_task
|
||||
.set(task_completed.eq(new_status))
|
||||
.execute(conn)
|
||||
pub async fn toggle_with_id(
|
||||
id: i32,
|
||||
connection: &PgPool,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE tasks
|
||||
SET completed = NOT completed
|
||||
WHERE id = $1
|
||||
"#,
|
||||
id
|
||||
)
|
||||
.execute(connection)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_with_id(id: i32, conn: &PgConnection) -> QueryResult<usize> {
|
||||
diesel::delete(all_tasks.find(id)).execute(conn)
|
||||
pub async fn delete_with_id(
|
||||
id: i32,
|
||||
connection: &PgPool,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
DELETE FROM tasks
|
||||
WHERE id = $1
|
||||
"#,
|
||||
id
|
||||
)
|
||||
.execute(connection)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
table! {
|
||||
tasks (id) {
|
||||
id -> Int4,
|
||||
description -> Varchar,
|
||||
completed -> Bool,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user