inverse_of
- ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法 - Hive Color
- inverse_of を使うと何ができるのか - 野生のエンジニアがあらわれた(チラ裏)
- ActiveRecord::Associations::ClassMethods
上記サイト様が上げているサンプルが分かりやすい
inverse_ofを使わなかった場合
class Dungeon < ActiveRecord::Base
has_many :traps
end
class Trap < ActiveRecord::Base
belongs_to :dungeon
end
d = Dungeon.first
t = d.traps.first
d.level == t.dungeon.level # => true
d.level = 10
d.level == t.dungeon.level # => false
d.object_id == t.dungeon.object_id #=> false
inverse_ofを使った場合
class Dungeon < ActiveRecord::Base
has_many :traps, :inverse_of => :dungeon
end
class Trap < ActiveRecord::Base
belongs_to :dungeon, :inverse_of => :traps
end
d = Dungeon.first
t = d.traps.first
d.level == t.dungeon.level # => true
d.level = 10
d.level == t.dungeon.level # => true
d.object_id == t.dungeon.object_id #=> true
つまり inverse_of を使うと、dとt.dungeonがメモリ内で同一のインスタンスを見るようになる。
with accepts_nested_attributes_for
accepts_nested_attributes_for の場合なぜ良いか。
親recordが出来る前に、associationをたどれるようになるのがよい。
inverse_ofを付けない場合
# app/models/survey.rb
class Survey
belongs_to :user
has_many :questions
accepts_nested_attributes_for :questions
end
# app/models/question.rb
class Question
validates :survey_id, presence: true
belongs_to :survey
end
こんなmodelをまとめてsaveすると
def create
@survey = current_user.surveys.build(survey_params)
if @survey.save
redirect_to @survey
else
render :new
end
end
子recordのvalidate時には、親recordができていないので、親をたどれない。
子recordのcreate時なら、親recordができているので、たどれるようになる。
子recordのbefore_validate内
[1] pry(#<Question>)> self
=> #<Question id: nil, survey_id: nil, name: "hoge", created_at: nil, updated_at: nil>
[2] pry(#<Question>)> survey
=> nil
親をたどれない
子recordのbefore_create内
[1] pry(#<Question>)> self
=> #<Question id: nil, survey_id: 1, name: "hoge", created_at: "2014-12-27 05:30:55", updated_at: "2014-12-27 05:30:55">
[2] pry(#<Question>)> survey
=> #<Survey id: 1, name: 'aaa', created_at: "2014-12-27 05:30:55", updated_at: "2014-12-27 05:30:55">
survey_idがあるので、before_createなら親をたどれる。
inverse_ofを付けた場合
# app/models/survey.rb
class Survey
belongs_to :user
has_many :questions, inverse_of: :survey
accepts_nested_attributes_for :questions
end
# app/models/question.rb
class Question
validates :survey, presence: true
belongs_to :survey, inverse_of: :questions
end
子recordのbefore_validate内
[1] pry(#<Question>)> self
=> #<Question id: nil, survey_id: nil, name: "hoge", created_at: nil, updated_at: nil>
survey_idはnilだけど。。。
[2] pry(#<Question>)> survey
=> #<Survey id: nil, name: 'aaa', created_at: nil, updated_at: nil>
before_validate段階で、親がたどれる!
しかもこの際に、SQLは発行されないです。(idでたどる訳じゃないから、そりゃそうか)
※ 投稿用に自分の検証環境のclass名などをSurveyとQuestionに書き換えちゃったので、なんか誤植あったらすいません。
inverse_ofがなくて困る事
何となく思うのは、外部制約キーを付けている場合。
あと、子のbefore_validate
内にて、親recordとの複合条件にてvalidateをしたい場合などに困りそう。
自分の場合は、親 > 子1 > 子2 の様な多重はrelationで、子2から親1の中身を使ってvalidateしよう、と思った
inverse_ofの制約
throughアソシエーションと一緒には動きません
polymorphicアソシエーションと一緒には動きません
for belongs_to associations has_many inverse associations are ignored.
らしいです。
参考
- Always use :inverse_of to connect two models in a belongs_to/has_many relationship - makandropedia
- inverse_of を使うと何ができるのか - 野生のエンジニアがあらわれた(チラ裏)
- Rails -- how to populate parent object id using nested attributes for child object and strong parameters? - Stack Overflow
- kueda.net / Blog » Blog Archive » validates_presence_of and accepts_nested_attributes_for
- ActiveRecord::Associations::ClassMethods
- ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法 - Hive Color