Add creation of posts with logging
This commit is contained in:
parent
56544cd0d4
commit
4948195495
13 changed files with 379 additions and 97 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
/rust_web.pdf
|
/rust_web.pdf
|
||||||
/rust_web.tex
|
/rust_web.tex
|
||||||
/simple_text_board/target
|
/simple_text_board/target
|
||||||
|
/simple_text_board/simple_text_board.sqlite
|
||||||
|
|
342
rust_web.org
342
rust_web.org
|
@ -260,112 +260,280 @@
|
||||||
*** Steps to reproduce :noexport:
|
*** Steps to reproduce :noexport:
|
||||||
|
|
||||||
**** Create new crate
|
**** Create new crate
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
cargo init simple_text_board
|
cargo init simple_text_board
|
||||||
cd simple_text_board
|
cd simple_text_board
|
||||||
rustup override set nightly
|
rustup override set nightly
|
||||||
cargo r
|
cargo r
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
**** Setup Rocket
|
**** Setup Rocket
|
||||||
Add Rocket dependency to ~Cargo.toml~:
|
***** Add Rocket to ~Cargo.toml~
|
||||||
#+BEGIN_SRC toml
|
#+BEGIN_SRC toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = "0.4"
|
rocket = "0.4"
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
**** Modify ~src/main.rs~ for Rockets "~Hello, world!~" program
|
***** Add ~Rocket.toml~
|
||||||
#+BEGIN_SRC rust
|
#+BEGIN_SRC toml
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
[development]
|
||||||
|
address = "0.0.0.0"
|
||||||
|
port = 8000
|
||||||
|
workers = 2
|
||||||
|
keep_alive = 5
|
||||||
|
log = "normal"
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
***** Modify ~src/main.rs~ for Rockets "~Hello, world!~" program
|
||||||
|
#+BEGIN_SRC rust
|
||||||
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
#[get("/")]
|
#[macro_use] extern crate rocket;
|
||||||
fn index() -> &'static str {
|
|
||||||
"Hello, world!"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
#[get("/")]
|
||||||
rocket::ignite().mount("/", routes![index]).launch();
|
fn index() -> &'static str {
|
||||||
}
|
"Hello, world!"
|
||||||
#+END_SRC
|
}
|
||||||
|
|
||||||
**** Add ~Rocket.toml~
|
fn main() {
|
||||||
#+BEGIN_SRC toml
|
rocket::ignite().mount("/", routes![index]).launch();
|
||||||
[development]
|
}
|
||||||
address = "0.0.0.0"
|
#+END_SRC
|
||||||
port = 8000
|
|
||||||
workers = 2
|
|
||||||
keep_alive = 5
|
|
||||||
log = "normal"
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
**** Create the base template at ~templates/base.html.tera~
|
**** Create first templates
|
||||||
#+BEGIN_SRC html
|
***** Create the base template at ~templates/base.html.tera~
|
||||||
<!DOCTYPE html>
|
#+BEGIN_SRC html
|
||||||
<html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<html>
|
||||||
<meta charset="utf-8" />
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta charset="utf-8" />
|
||||||
<title>simple text board</title>
|
<meta name="viewport" content="width=device-width" />
|
||||||
</head>
|
<title>simple text board</title>
|
||||||
<body>
|
</head>
|
||||||
<ul>
|
<body>
|
||||||
<li><a href="/">home</a></li>
|
<ul>
|
||||||
<li><a href="/create">create post</a></li>
|
<li><a href="/">home</a></li>
|
||||||
</ul>
|
<li><a href="/create">create post</a></li>
|
||||||
{% block content %} {% endblock %}
|
</ul>
|
||||||
</body>
|
{% block content %} {% endblock %}
|
||||||
</html>
|
</body>
|
||||||
#+END_SRC
|
</html>
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
**** Create the create template at ~templates/create.html.tera~
|
***** Create the create template at ~templates/create.html.tera~
|
||||||
#+BEGIN_SRC html
|
#+BEGIN_SRC html
|
||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>simple text board</h1>
|
<h1>simple text board</h1>
|
||||||
<h2>create a post</h2>
|
<h2>create a post</h2>
|
||||||
{% if flash %}
|
<form action="/create" method="post" accept-charset="utf-8">
|
||||||
<p>{{ flash }}</p>
|
<label for="author">author name</label>
|
||||||
{% endif %}
|
<input type="text" placeholder="author" name="author" required>
|
||||||
<form action="/create" method="post" accept-charset="utf-8">
|
|
||||||
<label for="author">author name</label>
|
|
||||||
<input type="text" placeholder="author" name="author" required>
|
|
||||||
|
|
||||||
<label for="email">email</label>
|
<label for="email">email</label>
|
||||||
<input type="text" placeholder="email" name="email">
|
<input type="text" placeholder="email" name="email">
|
||||||
|
|
||||||
<label for="title">title</label>
|
<label for="title">title</label>
|
||||||
<input type="text" placeholder="title" name="title" required>
|
<input type="text" placeholder="title" name="title" required>
|
||||||
|
|
||||||
<label for="content">content</label>
|
<label for="content">content</label>
|
||||||
<textarea type="text" placeholder="content" name="content" rows="8" cols="50" required>
|
<textarea type="text" placeholder="content" name="content" rows="8" cols="50" required>
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
<button type="submit">create</button>
|
<button type="submit">create</button>
|
||||||
</form>
|
{% if flash %}<p>{{ flash }}</p>{% endif %}
|
||||||
{% endblock content %}
|
</form>
|
||||||
#+END_SRC
|
{% endblock content %}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
**** Create the ~create_form~ function
|
***** Create the ~create_form~ function and add it to routing
|
||||||
#+BEGIN_SRC rust
|
#+BEGIN_SRC rust
|
||||||
use std::collections::HashMap;
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
use rocket::{get, request::FlashMessage, routes};
|
use std::collections::HashMap;
|
||||||
use rocket_contrib::templates::Template;
|
|
||||||
|
|
||||||
#[get("/create")]
|
use rocket::{get, request::FlashMessage, routes};
|
||||||
fn create_form(flash: Option<FlashMessage>) -> Template {
|
use rocket_contrib::templates::Template;
|
||||||
let mut context = HashMap::new();
|
|
||||||
|
|
||||||
if let Some(ref msg) = flash {
|
#[get("/create")]
|
||||||
context.insert("flash", msg.msg());
|
fn create_form(flash: Option<FlashMessage>) -> Template {
|
||||||
}
|
let mut context = HashMap::new();
|
||||||
|
|
||||||
Template::render("create", &context)
|
if let Some(ref msg) = flash {
|
||||||
}
|
context.insert("flash", msg.msg());
|
||||||
#+END_SRC
|
}
|
||||||
|
|
||||||
|
Template::render("create", &context)
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rocket::ignite()
|
||||||
|
.mount("/", routes![index, create_form])
|
||||||
|
.attach(Template::fairing())
|
||||||
|
.launch();
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
**** Setup Database and posts
|
||||||
|
***** Create the ~.env~ file
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
DATABASE_URL=simple_text_board.sqlite
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Add diesel to the dependencies and to ~rocket_contrib~ in ~Cargo.toml~
|
||||||
|
#+BEGIN_SRC toml
|
||||||
|
...
|
||||||
|
chrono = "0.4"
|
||||||
|
diesel = { version = "1.4", features = ["sqlite", "chrono"] }
|
||||||
|
log = "0.4"
|
||||||
|
...
|
||||||
|
[dependencies.rocket_contrib]
|
||||||
|
...
|
||||||
|
features = ["diesel_sqlite_pool", "tera_templates"]
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Configure database for Rocket in ~Rocket.toml~
|
||||||
|
#+BEGIN_SRC toml
|
||||||
|
[global.databases]
|
||||||
|
sqlite = { url = "simple_text_board.sqlite" }
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Create the first table schema
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
diesel setup
|
||||||
|
diesel migration generate create_posts
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** How to create the table
|
||||||
|
#+BEGIN_SRC sql
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
parent INTEGER,
|
||||||
|
timestamp DATETIME NOT NULL,
|
||||||
|
author VARCHAR NOT NULL,
|
||||||
|
email VARCHAR NOT NULL,
|
||||||
|
title VARCHAR NOT NULL,
|
||||||
|
content VARCHAR NOT NULL,
|
||||||
|
FOREIGN KEY (parent) REFERENCES posts(id)
|
||||||
|
);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** How to destroy it
|
||||||
|
#+BEGIN_SRC sql
|
||||||
|
DROP TABLE posts;
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Applay and test rollback
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
diesel migration run
|
||||||
|
diesel migration redo
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Create data model in ~src/models.rs~
|
||||||
|
#+BEGIN_SRC rust
|
||||||
|
use crate::schema::posts::{self, dsl::posts as table_posts};
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::{QueryResult, RunQueryDsl};
|
||||||
|
|
||||||
|
#[derive(Insertable)]
|
||||||
|
#[table_name = "posts"]
|
||||||
|
pub struct NewPost<'a> {
|
||||||
|
pub parent: Option<&'a i32>,
|
||||||
|
pub timestamp: NaiveDateTime,
|
||||||
|
pub author: &'a str,
|
||||||
|
pub email: &'a str,
|
||||||
|
pub title: &'a str,
|
||||||
|
pub content: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewPost<'_> {
|
||||||
|
pub fn insert(&self, conn: &diesel::SqliteConnection) -> QueryResult<usize> {
|
||||||
|
diesel::insert_into(table_posts).values(self).execute(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
***** Add create post functionality to ~src/main.rs~
|
||||||
|
#+BEGIN_SRC rust
|
||||||
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate diesel;
|
||||||
|
|
||||||
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
|
use diesel::SqliteConnection;
|
||||||
|
use log::{error, info};
|
||||||
|
use rocket::{
|
||||||
|
get, post,
|
||||||
|
request::{FlashMessage, Form},
|
||||||
|
response::{Flash, Redirect},
|
||||||
|
routes, uri, FromForm,
|
||||||
|
};
|
||||||
|
use rocket_contrib::{database, templates::Template};
|
||||||
|
|
||||||
|
mod models;
|
||||||
|
mod schema;
|
||||||
|
|
||||||
|
use models::NewPost;
|
||||||
|
|
||||||
|
#[database("sqlite")]
|
||||||
|
struct DbCon(SqliteConnection);
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct PostForm {
|
||||||
|
author: String,
|
||||||
|
email: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
#[post("/create", data = "<post>")]
|
||||||
|
fn create_post(
|
||||||
|
conn: DbCon,
|
||||||
|
remote_address: SocketAddr,
|
||||||
|
post: Form<PostForm>,
|
||||||
|
) -> Result<Redirect, Flash<Redirect>> {
|
||||||
|
let post = NewPost {
|
||||||
|
parent: None,
|
||||||
|
timestamp: Local::now().naive_local(),
|
||||||
|
author: &post.author.trim(),
|
||||||
|
email: &post.email.trim(),
|
||||||
|
title: &post.title.trim(),
|
||||||
|
content: &post.content.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match post.insert(&conn) {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("New post from {}", remote_address);
|
||||||
|
Ok(Redirect::to(uri!(index)))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not create post from {}: {:?}", remote_address, e);
|
||||||
|
Err(Flash::error(
|
||||||
|
Redirect::to(uri!(create_form)),
|
||||||
|
"Could not create post.".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rocket::ignite()
|
||||||
|
.mount("/", routes![index, create_form, create_post])
|
||||||
|
.attach(DbCon::fairing())
|
||||||
|
.attach(Template::fairing())
|
||||||
|
.launch();
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
* Conclusion
|
* Conclusion
|
||||||
|
|
||||||
|
|
1
simple_text_board/.env
Normal file
1
simple_text_board/.env
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DATABASE_URL=simple_text_board.sqlite
|
4
simple_text_board/Cargo.lock
generated
4
simple_text_board/Cargo.lock
generated
|
@ -270,6 +270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e2de9deab977a153492a1468d1b1c0662c1cf39e5ea87d0c060ecd59ef18d8c"
|
checksum = "3e2de9deab977a153492a1468d1b1c0662c1cf39e5ea87d0c060ecd59ef18d8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
"r2d2",
|
"r2d2",
|
||||||
|
@ -1163,6 +1164,9 @@ dependencies = [
|
||||||
name = "simple_text_board"
|
name = "simple_text_board"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"diesel",
|
||||||
|
"log 0.4.13",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_contrib",
|
"rocket_contrib",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,8 +6,11 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = "0.4"
|
rocket = "0.4"
|
||||||
|
chrono = "0.4"
|
||||||
|
diesel = { version = "1.4", features = ["sqlite", "chrono"] }
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
[dependencies.rocket_contrib]
|
[dependencies.rocket_contrib]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["tera_templates"]
|
features = ["diesel_sqlite_pool", "tera_templates"]
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
[global.databases]
|
||||||
|
sqlite = { url = "simple_text_board.sqlite" }
|
||||||
|
|
||||||
[development]
|
[development]
|
||||||
address = "0.0.0.0"
|
address = "0.0.0.0"
|
||||||
port = 8000
|
port = 8000
|
||||||
|
|
5
simple_text_board/diesel.toml
Normal file
5
simple_text_board/diesel.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE posts;
|
|
@ -0,0 +1,10 @@
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
parent INTEGER,
|
||||||
|
timestamp DATETIME NOT NULL,
|
||||||
|
author VARCHAR NOT NULL,
|
||||||
|
email VARCHAR NOT NULL,
|
||||||
|
title VARCHAR NOT NULL,
|
||||||
|
content VARCHAR NOT NULL,
|
||||||
|
FOREIGN KEY (parent) REFERENCES posts(id)
|
||||||
|
);
|
|
@ -1,9 +1,36 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
#[macro_use]
|
||||||
|
extern crate diesel;
|
||||||
|
|
||||||
use rocket::{get, request::FlashMessage, routes};
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
use rocket_contrib::templates::Template;
|
|
||||||
|
use chrono::Local;
|
||||||
|
use diesel::SqliteConnection;
|
||||||
|
use log::{error, info};
|
||||||
|
use rocket::{
|
||||||
|
get, post,
|
||||||
|
request::{FlashMessage, Form},
|
||||||
|
response::{Flash, Redirect},
|
||||||
|
routes, uri, FromForm,
|
||||||
|
};
|
||||||
|
use rocket_contrib::{database, templates::Template};
|
||||||
|
|
||||||
|
mod models;
|
||||||
|
mod schema;
|
||||||
|
|
||||||
|
use models::NewPost;
|
||||||
|
|
||||||
|
#[database("sqlite")]
|
||||||
|
struct DbCon(SqliteConnection);
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct PostForm {
|
||||||
|
author: String,
|
||||||
|
email: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/create")]
|
#[get("/create")]
|
||||||
fn create_form(flash: Option<FlashMessage>) -> Template {
|
fn create_form(flash: Option<FlashMessage>) -> Template {
|
||||||
|
@ -16,6 +43,36 @@ fn create_form(flash: Option<FlashMessage>) -> Template {
|
||||||
Template::render("create", &context)
|
Template::render("create", &context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/create", data = "<post>")]
|
||||||
|
fn create_post(
|
||||||
|
conn: DbCon,
|
||||||
|
remote_address: SocketAddr,
|
||||||
|
post: Form<PostForm>,
|
||||||
|
) -> Result<Redirect, Flash<Redirect>> {
|
||||||
|
let post = NewPost {
|
||||||
|
parent: None,
|
||||||
|
timestamp: Local::now().naive_local(),
|
||||||
|
author: &post.author.trim(),
|
||||||
|
email: &post.email.trim(),
|
||||||
|
title: &post.title.trim(),
|
||||||
|
content: &post.content.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match post.insert(&conn) {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("New post from {}", remote_address);
|
||||||
|
Ok(Redirect::to(uri!(index)))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not create post from {}: {:?}", remote_address, e);
|
||||||
|
Err(Flash::error(
|
||||||
|
Redirect::to(uri!(create_form)),
|
||||||
|
"Could not create post.".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index() -> &'static str {
|
fn index() -> &'static str {
|
||||||
"Hello, world!"
|
"Hello, world!"
|
||||||
|
@ -23,7 +80,8 @@ fn index() -> &'static str {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![index, create_form])
|
.mount("/", routes![index, create_form, create_post])
|
||||||
|
.attach(DbCon::fairing())
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
|
|
20
simple_text_board/src/models.rs
Normal file
20
simple_text_board/src/models.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::schema::posts::{self, dsl::posts as table_posts};
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::{QueryResult, RunQueryDsl};
|
||||||
|
|
||||||
|
#[derive(Insertable)]
|
||||||
|
#[table_name = "posts"]
|
||||||
|
pub struct NewPost<'a> {
|
||||||
|
pub parent: Option<&'a i32>,
|
||||||
|
pub timestamp: NaiveDateTime,
|
||||||
|
pub author: &'a str,
|
||||||
|
pub email: &'a str,
|
||||||
|
pub title: &'a str,
|
||||||
|
pub content: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewPost<'_> {
|
||||||
|
pub fn insert(&self, conn: &diesel::SqliteConnection) -> QueryResult<usize> {
|
||||||
|
diesel::insert_into(table_posts).values(self).execute(conn)
|
||||||
|
}
|
||||||
|
}
|
11
simple_text_board/src/schema.rs
Normal file
11
simple_text_board/src/schema.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
table! {
|
||||||
|
posts (id) {
|
||||||
|
id -> Integer,
|
||||||
|
parent -> Nullable<Integer>,
|
||||||
|
timestamp -> Timestamp,
|
||||||
|
author -> Text,
|
||||||
|
email -> Text,
|
||||||
|
title -> Text,
|
||||||
|
content -> Text,
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,6 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>simple text board</h1>
|
<h1>simple text board</h1>
|
||||||
<h2>create a post</h2>
|
<h2>create a post</h2>
|
||||||
{% if flash %}
|
|
||||||
<p>{{ flash }}</p>
|
|
||||||
{% endif %}
|
|
||||||
<form action="/create" method="post" accept-charset="utf-8">
|
<form action="/create" method="post" accept-charset="utf-8">
|
||||||
<label for="author">author name</label>
|
<label for="author">author name</label>
|
||||||
<input type="text" placeholder="author" name="author" required>
|
<input type="text" placeholder="author" name="author" required>
|
||||||
|
@ -17,9 +14,9 @@
|
||||||
<input type="text" placeholder="title" name="title" required>
|
<input type="text" placeholder="title" name="title" required>
|
||||||
|
|
||||||
<label for="content">content</label>
|
<label for="content">content</label>
|
||||||
<textarea type="text" placeholder="content" name="content" rows="8" cols="50" required>
|
<textarea type="text" placeholder="content" name="content" rows="8" cols="50" required></textarea>
|
||||||
</textarea>
|
|
||||||
|
|
||||||
<button type="submit">create</button>
|
<button type="submit">create</button>
|
||||||
|
{% if flash %}<p>{{ flash }}</p>{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
Loading…
Reference in a new issue