Skip to main content

ツイートを表示する

このページでは、ツイートを表示してみます。

取得用のモデルを実装する#

取得の構造体も定義してみます。下記は tweets テーブルから取得する場合の例です。

#[derive(Serialize, Queryable)] // --(1)struct TweetQueryable {    pub id: i32,    pub message: String,    pub posted_at: chrono::NaiveDateTime}

(1) で、保存用のモデルには Intertable を使っていましたが、取得時には Queryable を使います。

リポジトリを拡張する#

上記を踏まえ、 tweet エンティティをリスト取得するために、リポジトリに処理を追加します。

src/reporitories/tweets.rs
- use chrono::NaiveDateTime;+ use chrono::{NaiveDateTime, Local, TimeZone};  use diesel::prelude::*;  use crate::schema::tweets;  use crate::entities::Tweet;    #[derive(Serialize, Insertable)]  #[table_name="tweets"]  struct TweetInsertable {      pub message: String,      pub posted_at: NaiveDateTime  }    impl TweetInsertable {      fn from(tweet: &Tweet) -> TweetInsertable {          TweetInsertable {              message: tweet.message.to_string(),              posted_at: tweet.posted_at.naive_local()          }      }  }+ + #[derive(Serialize, Queryable)]+ struct TweetQueryable {+     pub id: i32,+     pub message: String,+     pub posted_at: NaiveDateTime+ }+ + impl TweetQueryable {+     fn map_entity(self) -> Tweet { // --(1)+         Tweet {+             id: self.id,+             account_name: "はなこ".to_string(),+             message: self.message,+             posted_at: Local.from_local_datetime(&self.posted_at).unwrap()+         }+     }+ }    pub fn store(db_conn: &PgConnection, tweet: &Tweet) -> Result<i32, String> {      let new_tweet = TweetInsertable::from(tweet);      diesel::insert_into(tweets::table)          .values(&new_tweet)          .returning(tweets::columns::id)          .get_result::<i32>(db_conn)          .map_err(|x| x.to_string())  }+ + pub fn list(db_conn: &PgConnection) -> Result<Vec<Tweet>, String> {+     tweets::table+         .load::<TweetQueryable>(db_conn)+         .map(|x| x.into_iter() // --(2)+             .map(|y| y.map_entity())+             .collect()+         )+         .map_err(|x| x.to_string())+ }

(1) についてですが、引数が &self ではなく self になっています。これは、自身の所有権をこのメソッドが引き受けて、フィールドの所有権をエンティティに引き渡すためです。つまり、このメソッドの呼び出しにより自身のオブジェクトは今後利用不可になります。下記のように参照方式で実装しても間違いではないので、どのような設計にしたいか、で、決めて良いです。

fn to_entity(&self) -> Tweet {    Tweet {        id: self.id,        account_name: "はなこ".to_string(),        message: self.message.to_string(),        posted_at: Local.from_local_datetime(&self.posted_at).unwrap()    }}

(2) も注意が必要です。 (1) で所有権を奪う設計にしたので、取得結果をイテレーションする際には、所有権を取得する into_iter() でイテレーションする必要があります。

その他の実装をする#

リポジトリの変更に合わせて、その他のファイルも修正していきます。保存時と同じような変更であり、特筆すべき点はないので、修正箇所のみ記載します。

src/services/tweet.rs
- use chrono::{Local, TimeZone};  use crate::entities::Tweet;  use crate::repositories::{tweets as tweet_repo};
- pub fn list() -> Vec<Tweet> {-     vec![-         Tweet {-             id: 1,-             account_name: "はなこ".into(),-             message: "2回目のツイート".into(),-             posted_at: Local.ymd(2020, 2, 20).and_hms(0, 0, 0)-         },-         Tweet {-             id: 2,-             account_name: "はなこ".into(),-             message: "はじめてのツイート".into(),-             posted_at: Local.ymd(2021, 2, 18).and_hms(0, 0, 0)-         }-     ]+ pub fn list(db_conn: &crate::DbConn) -> Result<Vec<Tweet>, String> {+     tweet_repo::list(&**db_conn)  }
...(省略)...
src/controllers/index.rs
  use rocket_contrib::templates::Template;  use crate::services::{tweets as tweets_service};    #[get("/")]- pub fn index() -> Template {-     let context = tweets_service::list();+ pub fn index(db_conn: crate::DbConn) -> Template {+     let context = tweets_service::list(&db_conn).unwrap();      Template::render("index", &context)  }
src/controllers/tweets.rs
  use rocket_contrib::templates::Template;  use rocket::request::Form;  use crate::services::{tweets as tweets_service};    #[derive(FromForm)]  pub struct NewTweet {      pub message: String  }    #[post("/", data = "<form>")]  pub fn post_tweet(db_conn: crate::DbConn, form: Form<NewTweet>) -> Template {      let _ = tweets_service::post(&db_conn, &form.message);-     let context = tweets_service::list();+     let context = tweets_service::list(&db_conn).unwrap();      Template::render("index", &context)  }

次のページでは、保存したツイートを削除したいと思います。