テーブルを関連付けて使う方法です。
環境は Postgres のバージョン 11 を使います。またその環境は Docker を用いて以下のコマンドで建てたサーバーを使います。
docker run \
--name postgres \
--detach \
--publish 5432:5432 \
--env POSTGRES_HOST_AUTH_METHOD=trust \
postgres:11
準備
テーブルを関連付けたいのでusers
とposts
という 2 つのテーブルを作ります。ユーザー 1 人辺り0-n
の投稿を持つようなものとして作ります。
ユーザーテーブル
ユーザーから作ります。
diesel migration generate create-users
up.sql
は以下のようにします。foo
とbar
という名前のユーザー名も一緒に作ります。
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(30) NOT NULL
);
INSERT INTO users (name) VALUES
('foo'),
('bar');
投稿テーブル
同じように投稿テーブルも作ります。
diesel migration generate create-posts
up.sql
はこのようにしました。本来はスネークケースが多いと思いますが、キャメルケースに対応した例の為にuserId
と名付けています。
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
"userId" INTEGER NOT NULL REFERENCES users(id)
);
INSERT INTO posts (title, "userId") VALUES
('hoge', 1),
('fuga', 2);
流します。
diesel migration run \
--database-url postgres://postgres:@localhost:5432
うまく処理が終わればsrc/schema.rs
が以下のように生成されます。
table! {
posts (id) {
id -> Int4,
title -> Varchar,
userId -> Int4,
}
}
table! {
users (id) {
id -> Int4,
name -> Varchar,
}
}
joinable!(posts -> users (userId));
allow_tables_to_appear_in_same_query!(
posts,
users,
);
また、こんな感じで投稿が取得できるようになります。
psql postgres://postgres:@localhost:5432 \
--tuples-only \
--command \
'
select json_agg(posts) from posts
where "userId" = (
select id from users where id = 1
)
' \
| jq .
この結果は JSON でこんな感じです。
[
{
"id": 1,
"title": "hoge",
"userId": 1
}
]
最後にsrc/lib.rs
でschema
を取り込めるようにしておきます。
#[macro_use]
extern crate diesel;
pub mod schema;
構造体作成
src/lib.rs
の最後から以下の 2 つの構造体を追記します。
use schema::{posts, users};
#[derive(Debug, Queryable, Identifiable)]
pub struct User {
pub id: i32,
pub name: String,
}
#[derive(Debug, Queryable, Associations, Identifiable)]
#[belongs_to(User, foreign_key = "userId")]
pub struct Post {
pub id: i32,
pub title: String,
#[column_name = "userId"]
pub user_id: i32,
}
気にする点は 4 つです。
Queryable
トレイトを継承Identifiable
トレイトを継承関連付けを行う方のテーブルは
Associations
トレイトを継承belongs_to
で属す先の構造体名を指定
またカラム名にキャメルケースを使っているような場合にはさらに 2 つあります。
構造体のプロパティはスネークケースで定義し、
column_name
で実際の名前を指定belongs_to
でさらにforeigh_key
としてcolumn_name
を指定
これは Diesel がbelongs_to(User)
のような時の場合、user_id
が参照キーだろうと見てしまうので、これを訂正しなければなりません。
ユーザーから投稿を取得する
src/main.rs
を書きます。
紐付けには対象の構造体から::belonging_to
関数を呼び出します。その際引数には属す先のインスタンスを指定します。
その後.load
や.get_results
などのメソッドを呼ぶことで、うまく行けば関連する投稿一覧が取得できます。
use diesel::pg::PgConnection;
use diesel::prelude::*;
use get_started_diesel_relation::{Post, User};
const DATABASE_URL: &'static str = "postgres://postgres:@localhost:5432";
fn main() {
let connection =
PgConnection::establish(DATABASE_URL).expect(&format!("Error connecting to {}", DATABASE_URL));
// 検索して取ってきた体
let user = User {
id: 1,
name: "foo".into(),
};
let post = Post::belonging_to(&user)
.load::(&connection)
.expect("Error in getting posts");
println!("{:#?}", post);
}
サンプルコード
動作確認できるコードは nju33-com/get-started-diesel-relation に置かれています。