ポリモーフィックの子から親を生成する
こんにちは。初めてお目にかかる方、初めまして。
@return_right34と申します。一応社員です。
最近、これまでとは打って変わった可愛らしいデザインのまとめサイトを制作している私ですが、
とある問題にぶつかりましたので、記事を書いてみました。
問題の親子
# app/models/article.rb class Article has_many :items end # app/models/item.rb class Item belongs_to :article belongs_to :itemable, polymorphic: true end # app/models/text.rb class Text has_one :item, as: :itemable end
こんな感じにポリモーフィックになっている親子構造を、
以下のような流れで一回のフォーム動作でデータ生成をしたいと考えました。
article -> items -> itemable(Text, ...etc)
……あれ? 子から親の作成って・・・? しかもポリモーフィックだし・・・。
結論から申しますと、以下のような感じでコードを追加すれば子から親&ポリモーフィックを生成できます。
Articleモデルはごく普通に
# app/models/article.rb class Article has_many :items accepts_nested_attributes_for :items end
特記することはありません。
Itemモデルをこんな感じに
# app/models/item.rb class Item accepts_nested_attributes_for :itemable def attributes=(attributes = {}) self.itamable_type = attributes[:itemable_type] super end def itemable_attributes=(attributes) self.itemable = itemable_type.constantize.create_with(attributes).find_or_initialize_by(id: attributes['id']) self.itemable.attributes = attributes.slice(*%w(content)) if itemable.id end end
驚いたのは、accepts_nested_attributes_forが、子から親の間柄でも働くこと。・・・そもそも、働かなかったら一回のform動作で終わらなくなってしまうのはそうなのですが。
下のメソッド二つは、ごく普通の親子関係なら必要なさそうなのですが、ポリモーフィックの場合は定義がされてないようなので、自分で追加します。
以下のURLが大変参考になりました。感謝してもしきれないです。
http://stackoverflow.com/questions/3969025/accepts-nested-attributes-for-with-belongs-to-polymorphic
コントローラはこんな感じに
# app/controllers/articles_controller.rb class ArticlesController def create @article = Articles.new(article_params) @article.save end private def article_params params[:article].permit(:title, items_attributes: [:itemable_type, itemable_attributes: [:content] ], ) end end
入れ子構造を重ねております。
モデルに"belongs_to :itemable, polymorphic: true"と記述した通りに、
子から親を生成する場合であっても{関連モデル名}_attributesと書けば通るみたいです。
フォームはこんな感じに
# app/views/articles/new.html.erb <%= simple_form_for @article do |f| %> <%= f.input :title %> <%= f.simple_fields_for :items do |item| %> <%= f.input :itemable_type, as: :hidden, input_html: { value: 'Text' } %> #ここ大事 <%= f.simple_fields_for :itemable, Text.new do |itemable| %> <%= itemable.input :content %> <% end %> <% end %> <% end %>
さりげなくsimple_form使っております。
コントローラではさらっと流しましたが、ポリモーフィックのタイプを明示しておくことが重要です。でなければ、親に何のクラスのインスタンスを生成するのかわからなくなってしまいます。
これで無事、articleからitems, itemsからitemable(Text, ...etc)を生成できるようになりました!
参考になる方がいらっしゃればと思います。
それでは、乱文失礼しました。