ActiveAdmin vs Nested Models vs Formtastic vs Acts as Relation
So lets set the scene. You are using Acts as Relation because it allows you to define inheritance within your models, in my case I had a Presentation which was extending a Resource
# app/models/presentation.rb
class Presentation < ActiveRecord::Base
acts_as :resource
attr_accessible :markup
end
# app/models/resource.rb
class Resource < ActiveRecord::Base
acts_as_superclass
belongs_to :topic
belongs_to :level
attr_accessible :name, :topic, :level
end
You will also notice that a Resource has a Topic and a Level that it belongs to.
# app/models/topic.rb
class Topic < ActiveRecord::Base
attr_accessible :name
has_many :resources
end
# app/models/level.rb
class Level < ActiveRecord::Base
attr_accessible :name
has_many :resources
end
Finally we want all of this to work nicely with the awesome ActiveAdmin gem, which creates a lovely administration front end to whichever models you define. In this case I wanted to have a 'Presentation' section where I could create a Presentation with a name, set its level and topic from drop downs of the currently available records for those two types, and finally write some markup. So there is stuff coming from a lot of different models, into the same display, and in ActiveAdmin's case this means working with Formtastic. The first thing that you need to do is generate your ActiveAdmin model
rails generate active_admin:resource Presentation
If you then jump into your admin area you will find it has create a Presentation section, but currently only contains the markup field that comes directly from the Presentation model. Adding name from the Resource model is quite simple, you just need to add as it hasn't been picked up by the ActiveAdmin generator.
# app/admin/presentations.rb
ActiveAdmin.register Presentation do
form do |f|
f.inputs do
f.input :name
f.input :markup
end
f.buttons
end
end
So now we want to add the level and topic selectors, which should pull in all possible entries in their respective models. Thankfully Formtastic has a rather nice way of doing this in a single line for each model
# app/admin/presentations.rb
...
f.input :name
f.input :level, :as => :select, :collection => Level.all, :include_blank => false, :selected => (presentation.level.id if !presentation.level.nil?)
f.input :topic, :as => :select, :collection => Topic.all, :include_blank => false, :selected => (presentation.topic.id if !presentation.topic.nil?)
f.input :markup
...
If you try the form at this point you will probably find that you get an ActiveRecord::AssociationTypeMismatch error, and this is down to Formtastic using the level and topic id reference within the form. In order to fix this one, you need to actually go into the controller of ActiveAdmin and modify its create method, again thankfully quite simple. At this point I will also add the index function of ActiveAdmin so that we see the correct fields in the index as well:
ActiveAdmin.register Presentation do
form do |f|
f.inputs do
f.input :name
f.input :level, :as => :select, :collection => Level.all, :include_blank => false, :selected => (presentation.level.id if !presentation.level.nil?)
f.input :topic, :as => :select, :collection => Topic.all, :include_blank => false, :selected => (presentation.topic.id if !presentation.topic.nil?)
f.input :markup
end
f.buttons
end
index do
column :name
column :level
column :topic
column :markup
default_actions
end
controller do
def create
@level = Level.find(params[:presentation][:level])
@topic = Topic.find(params[:presentation][:topic])
params[:presentation][:level] = @level
params[:presentation][:topic] = @topic
@presentation = Presentation.new(params[:presentation])
super
end
end
end
And that'll do it! Although this looks fairly overly complicated, it actually represents quite an intricate and easily extendable basis to create lots of different models made of the same bits.