Skip to content

Commit

Permalink
Merge pull request #446 from sunli829/master
Browse files Browse the repository at this point in the history
Add Poem example
  • Loading branch information
tyt2y3 authored Jan 23, 2022
2 parents 2e038a7 + 2110293 commit 946a03b
Show file tree
Hide file tree
Showing 18 changed files with 1,355 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
path: [basic, actix_example, actix4_example, axum_example, rocket_example]
path: [basic, actix_example, actix4_example, axum_example, rocket_example, poem_example]
steps:
- uses: actions/checkout@v2

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Join our Discord server to chat with others in the SeaQL community!
+ [Rocket Example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example)
+ [Actix Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example)
+ [Axum Example](https://github.com/SeaQL/sea-orm/tree/master/examples/axum_example)
+ [Poem Example](https://github.com/SeaQL/sea-orm/tree/master/examples/poem_example)

## Features

Expand Down
4 changes: 4 additions & 0 deletions examples/poem_example/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HOST=127.0.0.1
PORT=8000
#DATABASE_URL="mysql://root:@localhost/poem_example"
DATABASE_URL="sqlite::memory:"
27 changes: 27 additions & 0 deletions examples/poem_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "sea-orm-poem-example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]

[dependencies]
tokio = { version = "1.15.0", features = ["macros", "rt-multi-thread"] }
poem = { version = "1.2.33", features = ["static-files"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = { version = "1", features = ["derive"] }
tera = "1.8.0"
dotenv = "0.15"

[dependencies.sea-orm]
path = "../../" # remove this line in your own project
version = "^0.5.0"
features = ["macros", "runtime-tokio-native-tls", "debug-print"]
default-features = false

[features]
default = ["sqlx-sqlite"]
sqlx-mysql = ["sea-orm/sqlx-mysql"]
sqlx-postgres = ["sea-orm/sqlx-postgres"]
sqlx-sqlite = ["sea-orm/sqlx-sqlite"]
10 changes: 10 additions & 0 deletions examples/poem_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Poem with SeaORM example app

Edit `Cargo.toml` to use `sqlx-mysql` or `sqlx-postgres`.

```toml
[features]
default = ["sqlx-$DATABASE"]
```

Edit `.env` to point to your database.
163 changes: 163 additions & 0 deletions examples/poem_example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::env;

use poem::endpoint::StaticFilesEndpoint;
use poem::error::{BadRequest, InternalServerError};
use poem::http::StatusCode;
use poem::listener::TcpListener;
use poem::web::{Data, Form, Html, Path, Query};
use poem::{get, handler, post, EndpointExt, Error, IntoResponse, Result, Route, Server};
use sea_orm::{entity::*, query::*, DatabaseConnection};
use serde::Deserialize;
use tera::Tera;

mod post;
mod setup;

const DEFAULT_POSTS_PER_PAGE: usize = 5;

#[derive(Debug, Clone)]
struct AppState {
templates: tera::Tera,
conn: DatabaseConnection,
}

#[derive(Deserialize)]
struct Params {
page: Option<usize>,
posts_per_page: Option<usize>,
}

#[handler]
async fn create(state: Data<&AppState>, form: Form<post::Model>) -> Result<impl IntoResponse> {
post::ActiveModel {
title: Set(form.title.to_owned()),
text: Set(form.text.to_owned()),
..Default::default()
}
.save(&state.conn)
.await
.map_err(InternalServerError)?;

Ok(StatusCode::FOUND.with_header("location", "/"))
}

#[handler]
async fn list(state: Data<&AppState>, Query(params): Query<Params>) -> Result<impl IntoResponse> {
let page = params.page.unwrap_or(1);
let posts_per_page = params.posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE);
let paginator = post::Entity::find()
.order_by_asc(post::Column::Id)
.paginate(&state.conn, posts_per_page);
let num_pages = paginator.num_pages().await.map_err(BadRequest)?;
let posts = paginator
.fetch_page(page - 1)
.await
.map_err(InternalServerError)?;

let mut ctx = tera::Context::new();
ctx.insert("posts", &posts);
ctx.insert("page", &page);
ctx.insert("posts_per_page", &posts_per_page);
ctx.insert("num_pages", &num_pages);

let body = state
.templates
.render("index.html.tera", &ctx)
.map_err(InternalServerError)?;
Ok(Html(body))
}

#[handler]
async fn new(state: Data<&AppState>) -> Result<impl IntoResponse> {
let ctx = tera::Context::new();
let body = state
.templates
.render("new.html.tera", &ctx)
.map_err(InternalServerError)?;
Ok(Html(body))
}

#[handler]
async fn edit(state: Data<&AppState>, Path(id): Path<i32>) -> Result<impl IntoResponse> {
let post: post::Model = post::Entity::find_by_id(id)
.one(&state.conn)
.await
.map_err(InternalServerError)?
.ok_or_else(|| Error::from_status(StatusCode::NOT_FOUND))?;

let mut ctx = tera::Context::new();
ctx.insert("post", &post);

let body = state
.templates
.render("edit.html.tera", &ctx)
.map_err(InternalServerError)?;
Ok(Html(body))
}

#[handler]
async fn update(
state: Data<&AppState>,
Path(id): Path<i32>,
form: Form<post::Model>,
) -> Result<impl IntoResponse> {
post::ActiveModel {
id: Set(id),
title: Set(form.title.to_owned()),
text: Set(form.text.to_owned()),
}
.save(&state.conn)
.await
.map_err(InternalServerError)?;

Ok(StatusCode::FOUND.with_header("location", "/"))
}

#[handler]
async fn delete(state: Data<&AppState>, Path(id): Path<i32>) -> Result<impl IntoResponse> {
let post: post::ActiveModel = post::Entity::find_by_id(id)
.one(&state.conn)
.await
.map_err(InternalServerError)?
.ok_or_else(|| Error::from_status(StatusCode::NOT_FOUND))?
.into();
post.delete(&state.conn)
.await
.map_err(InternalServerError)?;

Ok(StatusCode::FOUND.with_header("location", "/"))
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "debug");
tracing_subscriber::fmt::init();

// get env vars
dotenv::dotenv().ok();
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file");
let host = env::var("HOST").expect("HOST is not set in .env file");
let port = env::var("PORT").expect("PORT is not set in .env file");
let server_url = format!("{}:{}", host, port);

// create post table if not exists
let conn = sea_orm::Database::connect(&db_url).await.unwrap();
let _ = setup::create_post_table(&conn).await;
let templates = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap();
let state = AppState { templates, conn };

println!("Starting server at {}", server_url);

let app = Route::new()
.at("/", post(create).get(list))
.at("/new", new)
.at("/:id", get(edit).post(update))
.at("/delete/:id", post(delete))
.nest(
"/static",
StaticFilesEndpoint::new(concat!(env!("CARGO_MANIFEST_DIR"), "/static")),
)
.data(state);
let server = Server::new(TcpListener::bind(format!("{}:{}", host, port)));
server.run(app).await
}
18 changes: 18 additions & 0 deletions examples/poem_example/src/post.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
#[sea_orm(table_name = "posts")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
pub id: i32,
pub title: String,
#[sea_orm(column_type = "Text")]
pub text: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
33 changes: 33 additions & 0 deletions examples/poem_example/src/setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use sea_orm::sea_query::{ColumnDef, TableCreateStatement};
use sea_orm::{error::*, sea_query, ConnectionTrait, DbConn, ExecResult};

async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
let builder = db.get_database_backend();
db.execute(builder.build(stmt)).await
}

pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(super::post::Entity)
.if_not_exists()
.col(
ColumnDef::new(super::post::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(
ColumnDef::new(super::post::Column::Title)
.string()
.not_null(),
)
.col(
ColumnDef::new(super::post::Column::Text)
.string()
.not_null(),
)
.to_owned();

create_table(db, &stmt).await
}
Loading

0 comments on commit 946a03b

Please sign in to comment.