LoginSignup
54
30

More than 5 years have passed since last update.

Rails 5 以上 + ActiveRecord での inverse_of オプションの使いどころ

Last updated at Posted at 2017-12-23

ActiveRecord を単体で使う場合と違って、Rails 5 と組み合わせた場合 belongs_to の指すインスタンスが必須になり、この変更によって1対多のリレーションで1の方を保存する前に多の方のインスタンスを作って1の方の保存のタイミングで連動して多の方も保存するみたいな、これまで普通に通っていた処理がひっかかるようになります。

require "active_record"
ActiveRecord::VERSION::STRING   # => "5.1.4"

# Rails 5 以上と一緒に使う場合は belongs_to の required: true を最初から有効にされる
ActiveRecord::Base.belongs_to_required_by_default = true

ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
  create_table :articles do |t|
  end
  create_table :comments do |t|
    t.belongs_to :commentable, polymorphic: true, null: false
  end
end

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

article = Article.new
article.comments.build
article.save! rescue $! # => #<ActiveRecord::RecordInvalid: Validation failed: Comments is invalid>

そこで、可能なら Article.create! して親の方を先に作っておくとか、ActiveRecord::Base.belongs_to_required_by_default = false して設定を無効にするとか、次のように required: false をつけてもいいですが、

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true, required: false
end

article = Article.new
article.comments.build
article.save!           # => true

inverse_of オプションを使って Article から見た comments たちに commentable が欲しいときはDBから探すのではなく commentable メソッドを見てくれと伝えます。これで通るようになります。また副作用というか、こっちが本来の目的かもしれませんが無駄なSQLを減らせます。

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable, inverse_of: :commentable
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

article = Article.new
article.comments.build
article.save!           # => true

もし混乱してきたら子の belongs_to :xxx の :xxx の部分を親の inverse_of に指定すると覚えておくと楽です。

参考

[rails] accepts_nested_attributes_for の場合は、inverse_of を付けておくと良い気がする
https://qiita.com/ryoff/items/e3ba4b8c8be117c79b73
※こちらの記事では polymorphic では動作しないと言及がありますが今では動くようになっていました

54
30
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
54
30