概要
先日の仕事中、module内にあるActive Recordのモデルに対してassociationを作成しようとしたときに少し苦戦したので、その経緯をまとめます。
やり方だけ知りたい方は結論へどうぞ。
経緯
前提
次のような3つのモデルを作成し、Buzz
がFizz::Foo
、Fizz::Foo
がFizz::Bar
をそれぞれhas_many
で持つようにしたい。
app/models/fizz.rb
module Fizz
def self.table_name_prefix
'fizz_'
end
end
app/models/buzz.rb
class Buzz < ApplicationRecord
end
app/models/fizz/bar.rb
class Fizz::Bar < ApplicationRecord
end
app/models/fizz/foo.rb
class Fizz::Foo < ApplicationRecord
end
試したこと1 - テーブル名と同じprefixをつけてhas_many
で指定する
まず最初に、テーブル名と同じprefixであるfizz_
をhas_many
で指定してみました。
app/models/buzz.rb
class Buzz < ApplicationRecord
has_many :fizz_foos
end
app/models/fizz/bar.rb
class Fizz::Bar < ApplicationRecord
belongs_to :fizz_foo
end
app/models/fizz/foo.rb
class Fizz::Foo < ApplicationRecord
has_many :fizz_bars
belongs_to :buzz
end
結果
メソッド | 結果 | |
---|---|---|
Buzz.first.fizz_foos |
NameError (uninitialized constant Buzz::FizzFoo) が発生 |
× |
Fizz::Foo.first.fizz_bars |
NameError (uninitialized constant Fizz::Foo::FizzBar) が発生 |
× |
Fizz::Foo.first.buzz |
#<Buzz id: 1, created_at: "2019-12-11 14:40:32", updated_at: "2019-12-11 14:40:32"> |
○ |
Fizz::Bar.first.fizz_foo |
nil |
× |
Fizz::Foo.first.buzz
のときしか正しく関係先が取得できなかった。
試したこと2 - prefixを外す
moduleに無いモデル同士のときは、並列のモデルが解決できているのに、1.ではuninitialized constant Fizz::Foo::FizzBar
と親モデル
::has_manyで指定した名前
となっている。
それを元に考えると、同じmodule内のモデル同士であればprefixがなくても解決できるのではと思い、試してみました。
app/models/buzz.rb
class Buzz < ApplicationRecord
has_many :fizz_foos
end
app/models/fizz/bar.rb
class Fizz::Bar < ApplicationRecord
belongs_to :foo
end
app/models/fizz/foo.rb
class Fizz::Foo < ApplicationRecord
has_many :bars
belongs_to :buzz
end
結果
メソッド | 結果 | |
---|---|---|
Buzz.first.fizz_foos |
NameError (uninitialized constant Buzz::FizzFoo) が発生 |
× |
Fizz::Foo.first.bars |
#<ActiveRecord::Associations::CollectionProxy [#<Fizz::Bar id: 1, foo_id: 1, created_at: "2019-12-11 14:43:27", updated_at: "2019-12-11 14:43:27">]> |
○ |
Fizz::Foo.first.buzz |
#<Buzz id: 1, created_at: "2019-12-11 14:40:32", updated_at: "2019-12-11 14:40:32"> |
○ |
Fizz::Bar.first.foo |
#<Fizz::Foo id: 1, buzz_id: 1, created_at: "2019-12-11 14:40:51", updated_at: "2019-12-11 14:40:51"> |
○ |
Fizz
モジュール内同士の関係は正しく取れるようになったが、別モジュールからの時はまだエラーが起きている。
試したことに3 - class_name
を使う
下記2つを参考に別モジュールのときにclass_name
を使用するように変更。
https://stackoverflow.com/questions/25715426/rails-associations-with-modules
https://railsguides.jp/association_basics.html
app/models/buzz.rb
class Buzz < ApplicationRecord
has_many :fizz_foos, class_name: 'Fizz::Foo'
end
app/models/fizz/bar.rb
class Fizz::Bar < ApplicationRecord
belongs_to :foo
end
app/models/fizz/foo.rb
class Fizz::Foo < ApplicationRecord
has_many :bars
belongs_to :buzz
end
結果
メソッド | 結果 | |
---|---|---|
Buzz.first.fizz_foos |
#<ActiveRecord::Associations::CollectionProxy [#<Fizz::Foo id: 1, buzz_id: 1, created_at: "2019-12-11 14:40:51", updated_at: "2019-12-11 14:40:51">]> |
○ |
Fizz::Foo.first.bars |
#<ActiveRecord::Associations::CollectionProxy [#<Fizz::Bar id: 1, foo_id: 1, created_at: "2019-12-11 14:43:27", updated_at: "2019-12-11 14:43:27">]> |
○ |
Fizz::Foo.first.buzz |
#<Buzz id: 1, created_at: "2019-12-11 14:40:32", updated_at: "2019-12-11 14:40:32"> |
○ |
Fizz::Bar.first.foo |
#<Fizz::Foo id: 1, buzz_id: 1, created_at: "2019-12-11 14:40:51", updated_at: "2019-12-11 14:40:51"> |
○ |
正しく動くようになった!
結論
- 同じモジュール内のassociationの時、モジュールがない場合と同様にモジュールを除いたクラス名で作成できる
- 別モジュールのモデル間のassociationの時、
class_name
でモジュールを含めたクラス名を指定する
ちなみに
そもそも、この記事を書くために再度調べていたら、Ruby on Rails API にしっかり記載がありました。
By default, associations will look for objects within the current module scope.