外に出るねくら

~ 外に出たって結局やることは自宅と同じ ~

同じモデルに対する外部参照キーを別名で複数持つ方法

同じモデルに対する外部参照キーを複数持つ方法

今週に入って、だいたい10度前後を推移していた(高い時は20度近かった)のに、この日の最高気温2度。
寒すぎ。
どうでもいいですね。
さて、今回は同じモデルに対する外部参照キーを複数持つ方法、そして外部参照キーの名前をデフォルトから変更する方法を一気にやります。

言葉だけだとわかりにくいのでイメージを説明します。

何がしたいのか

f:id:Kenny27:20190209090943p:plain
あるユーザは0以上のPostを持っている

あるユーザが複数のPostを持っているようなモデルを想像する。
誰から誰に投稿したのか、どのグループに投稿したのかを保持したくて

  • from_user_id
  • to_user_id
  • posted_group_id

を持たせた。

from_user_idto_user_id がともにUserを参照してほしい。
という感じです。

HOW TO

テーブル作成時にカラム名を変更しておく

外部参照をするときは、

class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :text
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end

のように書くのがオーソドックス。

そして実際に作成されるテーブルには user_id でカラムが作成される。
ただ今回みたいに from_user_idto_user_id という別々のカラムを作成し、それぞれでUserを参照したい場合、同じカラムがPostsテーブル内に2つ存在することになるので困る。
なので user_id というカラムを作るのをやめてもらう。

class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :text
      t.references :from_user, foreign_key: { to_table: :users }
      t.references :to_user, foreign_Key: { to_table: :users }

      t.timestamps
    end
  end
end

このように書くことで、Postsテーブルには from_user_idto_user_id というカラムができる。

これでOK。マイグレートする。

Modelにアソシエーションを追加

もちろんこれだけではダメで、Modelにアソシエーションを記載しないといけない。

class Post < ApplicationRecord
  belongs_to :from_user, class_name: 'User', :foreign_key => 'from_user_id'
  belongs_to :to_user, class_name: 'User', :foreign_key => 'to_user_id'
  has_many :praise_replies
end  

アソシエーションを記述する際は、 belongs_to :${table_name} がオーソドックスだが、今回はUserを参照しているカラムが2つあるのでそれぞれに対してアソシエーションの定義が必要。

これで例えば

posts = Post.all.includes(:from_user)
posts.each do |post|
 puts post.from_user.name
 puts post.from_user.email
end

とするとPostに紐づくfrom_userのUser情報をワンクエリで取ってくることができる。

以上です。

いつもこれくらいサバサバした文章を書きたいな。。。
ありがとうございました。