LoginSignup
36
31

More than 5 years have passed since last update.

ActiveRecord Association 覚え書き

Last updated at Posted at 2014-06-25

概要

ActiveRecord の Association 周りの理解が曖昧。
整理のためにまとめておこうと思います。
随時加筆予定。

外部キーをどう持つか(子)

User -(1:N)- Order な関係を考える。
Order が User の id を外部キーとして持つ場合、
子である Order の実装方法として以下の 3 パターンが考えられる。

Pattern DB column name association name
パターン C1 user_id user
パターン C2 customer_id customer
パターン C3 user_id customer

パターン C1

一番素直なパターン。楽。
user_id というカラムを持ち、
関連の名前も素直に user とするパターン。

20140623060050_create_orders.rb
def change
  create_table :orders do |t|
    t.belongs_to :user, index:true
    ...
  end
end
models/order.rb
class Order < ActiveRecord::Base
  belongs_to :user
  ...
end

パターン C2

関連性を表すことのできる、直感的な名前を付けたいパターン。
customer_id というカラムを持ち、
関連の名前も customer とする。

class_name を指定する必要がある。

20140623060050_create_orders.rb
def change
  create_table :orders do |t|
    t.belongs_to :customer, index:true
    ...
  end
end
models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "User"
  ...
end

パターン C3

関連性を表すために直感的な名前を付けたいパターンその2。
しかし、カラム名は user_id とし、
アプリから関連を辿る際には customer を利用する。
(あんまりないかも)

class_name および foreign_key を指定してやる必要がある。

20140623060050_create_orders.rb
def change
  create_table :orders do |t|
    t.belongs_to :user, index:true
    ...
  end
end
models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "User", foreign_key: "user_id"
  ...
end

子との関連をどう設定するか(親)

先ほどと同様に、User -(1:N)- Order な関係を考える。
親である User の関連付けの命名方法として以下の 2 パターンが考えられる。

Pattern association name
パターン P1 orders
パターン P2 transactions

パターン P1

関連の名前を素直に子のモデル名の複数形とする素直なパターン。

models/user.rb
class User < ActiveRecord::Base
  has_many :orders
  ...
end

パターン P2

関連の名前を子のモデル名とはベツモノにするパターン。

class_name を指定する必要がある。

models/user.rb
class User < ActiveRecord::Base
  has_many :transactions, class_name: 'Order'
  ...
end

ちょっとややこしいケース

パターン C2 とパターン P2 を同時にする場合。
つまり、親への外部キー名を(親のモデル名ではない)独自のものにし、
かつ、子との関連名を(子のモデル名ではない)独自のものに設定したい場合。

親に foreign_key を設定してやる必要がある。

20140623060050_create_orders.rb
def change
  create_table :orders do |t|
    t.belongs_to :customer, index:true
    ...
  end
end
models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "User"
  ...
end
models/user.rb
class User < ActiveRecord::Base
  has_many :transactions, class_name: 'Order', foreign_key: 'customer_id'
  ...
end

inverse_of

TODO

foreign_key

belongs_to の場合

デフォルトでは Rails は外部キーとして 関連名_id を想定している。
DB のカラム名が 関連名_id となっていない場合はちゃんと指定してあげること。
(あんまりないと思う)

例: (上記 パターン C3 より)

models/order.rb
class Order < ActiveRecord::Base
  # デフォルトでは customer_id を利用しようとするので
  # user_id を使うよう foreign_key を明示する
  belongs_to :customer, class_name: "User", foreign_key: "user_id"
  ...
end

has_many の場合

デフォルトでは Rails は外部キーとして 自モデル名_id を想定している。
DB のカラム名が 自モデル名_id となっていない場合はちゃんと指定してあげること。

models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "User"
  ...
end
models/user.rb
class User < ActiveRecord::Base
  # デフォルトでは user_id を利用しようとするので
  # ちゃんと指定してやる
  has_many :transactions, class_name: 'Order', foreign_key: 'customer_id'
  ...
end

foreign_key を適切に設定していない場合、
以下のようなケースでエラーとなりうる。

# エラーが発生する
#     ActiveRecord::UnknownAttributeError:
#       unknown attribute: user_id
user.transactions.build(order_param)
36
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
31