I am trying to build a CRUD controller and form in Rails 3.
I have
class Publication < ActiveRecord::Base
has_many :posts
end
where Posts is a STI model :
class Post < ActiveRecord::Based
attr_accessible :title, :description
end
and I have a few inherited models:
class Image < Post
end
class Video < Post
end
class Status < Post
end
etc.
I want to create a CRUD for Publication, where the user is able to add as many Posts as they want, dynamically adding nested form for any type of Post.
Is there a gem I could use that supports such nested forms with STI ?
I tried to build a form, but I need to amend the Publication class and introduce nested attributes for each additional inherited model. Is there a way to avoid doing this?
class Publication < ActiveRecord::Base
has_many :videos, :dependent => :destroy
accepts_nested_attributes_for :videos, allow_destroy: true
attr_accessible :videos_attributes
has_many :posts
end
I wrote a short blog post about this problem at: http://www.powpark.com/blog/programming/2014/05/07/rails_nested_forms_for_single_table_inheritance_associations
I basically decided to use the cocoon
gem, which provides two helper methods - link_to_add_association
and link_to_remove_association
, which dynamically add the respective decorated class fields to the form (such as Post
, Image
or Video
# _form.html.haml
= simple_form_for @publication, :html => { :multipart => true } do |f|
= f.simple_fields_for :items do |item|
= render 'item_fields', :f => item
= link_to_add_association 'Add a Post', f, :items, :wrap_object => Proc.new { |item| item = Item.new }
= link_to_add_association 'Add an Image', f, :items, :wrap_object => Proc.new { |item| item = Image.new }
= link_to_add_association 'Add a Video', f, :items, :wrap_object => Proc.new { |item| item = Video.new }
= f.button :submit, :disable_with => 'Please wait ...', :class => "btn btn-primary", :value => 'Save'
The :wrap_object
proc generates the correct object, to be rendered inside _item_fields
partial, like:
# _item_fields.html.haml
- if f.object.type == 'Video'
= render 'video_fields', :f => f
- elsif f.object.type == 'Image'
= render 'image_fields', :f => f
- elsif f.object.type == 'Post'
= render 'post_fields', :f => f