[フレーム]
Last Updated: February 25, 2016
·
8.966K
· royletron

ActiveAdmin vs Nested Models vs Formtastic vs Acts as Relation

Picture

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.

AltStyle によって変換されたページ (->オリジナル) /