Edited at

ActiveRecord Association 覚え書き

More than 5 years have passed since last update.


概要

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)