5170


itemモデルを作成しuserモデルにmain_itemとsub_itemの２つのアイテムリストを結びつけようと思い、以下のようにしてみました。

[code]

    class User < ActiveRecord::Base
      has_many :main_items, :as => :itemable, :class_name => "Item"
      has_many :sub_items, :as => :itemable, :class_name => "Item"
    end
    
[/code]

そのあと、それぞれのitemsに要素を追加してみたのすが、内部的にmainとsubが指す要素が同一のものになってしまい、どちらか片方に要素を追加すると、もう片方にも要素が追加されてしまうという現象が発生しました。

[code]

    # user = User.new
    # user.main_items.new name:'sword'
    # user.sub_items.new name:'shield'
    # user.save
    # user.main_item.count
    => 2
    
[/code]

mainとsubのアイテムリスト独立してuserに結びつけたい場合には、どのようにすればよいのでしょうか。


CC BY-SA 3.0
2015-01-23T12:21:42.110
5158
2015-01-23T16:10:53.673
2015-01-23T12:54:52.050
5840
5840
question
rails-activerecord
rails4で２つの同じモデルをhas_manyで結びつけるには？


main_item と sub_item でクラスを分けます。別のモデルを作成しても良いのですが、単一テーブル継承 (STI)
を利用すると扱いやすいでしょう。ここでは、Item を継承して Item::Main, Item::Sub を作ります。

分かりやすいように、以下の例では User, Item いずれにも name というプロパティがあることを前提に書いてあります。

  1. STIのためにマイグレーションを作成します。
[code]     rails g migration AddTypeToItems type:string

    rake db:migrate
    
[/code]

type には所属するクラス名 ("Item::Main" または "Item::Sub" が入ります)

  2. モデルをいろいろいじります。
[code]     # app/models/item/main.rb

    class Item::Main < Item
    end
    
[/code]

[code]     # app/models/item/sub.rb

    class Item::Sub < Item
    end
    
[/code]

[code]     # app/models/user.rb

    class User < ActiveRecord::Base
      has_many :main_items, :as => :itemable, :class_name => "Item::Main"
      has_many :sub_items, :as => :itemable, :class_name => "Item::Sub"
      has_many :items, :as => :itemable
    end
    
[/code]

便利そうなので items でどちらの種類の item も取れるようにしておきました。

  3. 試します。
[code]     user = User.create name: "Fate"

    user.main_items.create name: "school uniform"
    user.main_items.create name: "barrier jacket"
    user.sub_items.create name: "pencil"
    user.sub_items.create name: "notebook"
    
    user.main_items.pluck(:id, :name)
    # => [[1, "school uniform"], [2, "barrier jacket"]]
    user.sub_items.pluck(:id, :name)
    # => [[3, "pencil"], [4, "notebook"]]
    user.items.pluck(:id, :name)
    # => [[1, "school uniform"], [2, "barrier jacket"], [3, "pencil"], [4, "notebook"]]
    
[/code]

よい感じですね。

ちなみに、Item として検索しても、Item::Main または Item::Sub のインスタンスが返ってきます。(User#items
で取得した場合も同じ)

[code]

    Item.find 1
    # => #<Item::Main id: 1, name: "school uniform", itemable_id: 1, itemable_type: "User", created_at: "2015-01-23 15:58:26", updated_at: "2015-01-23 15:58:26", type: "Item::Main">
    
[/code]


CC BY-SA 3.0
2015-01-23T16:10:53.673
5170
2015-01-23T16:10:53.673
76
5158
answer