Rustを勉強するためにactix-webを使ってwebアプリを書いてみた備忘録。
github.com
今日やったこと
Rust初心者で何も分からないので、actix-web と diesel のGetting Startedを読みながら、雰囲気でコードを書いてみた。
DBの作成・マイグレーションなどをするため、cliをインストールする必要がある。
$ cargo install diesel_cli
で普通にインストールできた。
もし postgresql とかがローカルになければ、sqlite だけのインストールもできるらしい。
$ cargo install diesel_cli --no-default-features --features sqlite
DBの作成
DATABASE_URLを指定して diesel setup
するとDBが作れる。
$ DATABASE_URL=development.sqlite3 diesel setup
毎回指定するのも面倒なので、.envrc に入れておく。
$ echo export DATABASE_URL=development.sqlite3 > .envrc
diesel migration generate
で作成できる。
$ diesel migration generate create_posts
Creating migrations/2019-10-27-152149_create_posts/up.sql
Creating migrations/2019-10-27-152149_create_posts/down.sql
ActiveRecordみたいにRubyではなくて、生SQLだった。この潔さは嫌いじゃない。
diesel migration run
でマイグレーションを実行してくれる。
$ diesel migration run
Running migration 2019-10-27-152149_create_posts
diesel migration redo
で down.sql の動作確認もできる。
DBの接続
こんな感じで connection を作る。
pub fn establish_connection() -> SqliteConnection {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
SqliteConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}
コネクションの管理を diesel でやっているのか、自分でやる必要あるのかはよく分かっていない。
複数のPostを取得する
こんな感じでfilterやlimitが書けるらしい。たぶんorderとかも使える気がする。
let results = posts
.filter(published.eq(true))
.limit(5)
.load::<Post>(&connection)
.expect("Error loading posts");
1つのPostを取得する
findのあとにfirstを書くのが冗長感あるけど、たぶん posts.find(id)
がWHERE句になるんだと思う。
let post = posts.find(id)
.first::<Post>(&connection)
.expect("Error finding posts");
Postを登録する
insert_intoを使う。
ちなみにdieselのGetting Started だとget_resultを使っているけど、sqliteは対応してないので代わりにexecuteを使っている。
let new_post = NewPost {
title: "title",
body: "body",
};
let connection = establish_connection();
diesel::insert_into(posts::table)
.values(&new_post)
.execute(&connection)
.expect("Error saving new post");
Postを更新する
Getting Startedを参考にして書いたら、普通に動いた。
diesel::update(posts.find(id))
.set(published.eq(true))
.execute(&connection)
.expect(&format!("Unable to find post {}", id));
Postを削除する
これもドキュメント通り。
diesel::delete(posts.find(id))
.execute(&connection)
.expect("Error deleting posts");
ルーティング周り
それっぽく書いたら、動いた。
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/posts", web::get().to(posts_index))
.route("/posts/{id}", web::get().to(posts_show))
.route("/posts", web::post().to(posts_create))
.route("/posts/{id}", web::put().to(posts_update))
.route("/posts/{id}", web::patch().to(posts_update))
.route("/posts/{id}", web::delete().to(posts_destroy))
})
次にやりたいこと
- SQLに関する処理をModelに移す(もしくはRepositoryを作る)
- テストコードを書く
- jsonを返すように直す
- テンプレートエンジンを使ってhtmlを返す