ややめも

アプリ作りたい女子大学院生のめも💁‍♀️

技術メモ
日記
つくったもの
就活の話

Railsで同じモデルを参照する外部キーを2つ以上もつ方法

Railsにて、同じモデルを参照する外部キーを作成するときに、
デフォルトでリレーションを定義してしまうと、カラム名がテーブル名_idになってしまい、複数カラムを設定したいときにうまくいかない。
例えばユーザー間取引の時などに、ユーザーテーブルを参照する二人の取引者の両方をモデルに関連付ける方法を考える。

class CreateTransactions < ActiveRecord::Migration[5.2]
  def change
    create_table :transactions do |t|
      t.string :text, null: false

      t.references :item, foreign_key: true. null: false

      t.references :buyer, foreign_key: { to_table: :users }, null: false
      t.references :seller, foreign_key: { to_table: :users }, null: false

      t.timestamps
    end
  end
end

マイグレーションファイルの書き方

# 基本の書き方
t.references :item, foreign_key: true

# カラム名を指定する書き方

t.references :user1, foreign_key: { to_table: :users }, null: false
 t.references :user2, foreign_key: { to_table: :users }, null: false

基本の書き方では、itemsテーブルを参照していて、カラム名は自動的にitem_idとなる。

関連付けの名前をテーブル名以外にしてしまうと、Rails側がどのテーブルを参照しているか推論できない。
to_tableで参照先テーブルを指定しておくとしてやれば、別名でカラム名を指定することができる。
このサンプルだとuser1_iduser2_idというカラムが作成される。

モデル定義

# Userモデル

class User < ApplicationRecord
    has_many :user1_transactions, class_name: 'Transaction', foreign_key: 'user1_id'
    has_many :user2_transactions, class_name: 'Transaction', foreign_key: 'user2_id'
end

# Transactionモデル

class Transaction < ApplicationRecord
    belongs_to :user1, class_name: 'User', foreign_key: 'user1_id'
    belongs_to :user2, class_name: 'User', foreign_key: 'user2_id'
end

最初に関連付けの名前を'transaction'でやろうとしたら以下のエラーが出てうまくいかなかった。

"You tried to define an association named transaction on the model Category, but this will conflict with a method transaction already defined by Active Record. Please choose a different association name."

つまり、ActiveRecordで'transaction'というメソッドがすでに定義されているからこの単語は使えないらしいので別名で定義。

railsguides.jp

Railsガイドには関連付けのオプションが詳しく書いているので、それぞれを確認する。
今回使っているオプションはclass_nameforeign_key

  • class_name : 関連付けの名前からモデルを推定できないときにクラス名を指定することができる。
  • foreign_key : 外部キーの名前を直接指定できる。


こんな感じでやっていくうとうまく関連づけられるはず。


rails-erdとかでリレーションを確認してみたらうまくできてそうだった。