Sti, One Controller

STI, one controller

First. Add some new routes:

resources :highlights, :controller => "ads", :type => "Highlight"
resources :bargains, :controller => "ads", :type => "Bargain"

And fix some actions in AdsController. For example:

def new
@ad =
@ad.type = params[:type]

For best approach for all this controller job look this comment

That's all. Now you can go to localhost:3000/highlights/new and new Highlight will be initialized.

Index action can look like this:

def index
@ads = Ad.where(:type => params[:type])

Go to localhost:3000/highlights and list of highlights will appear.

Same way for bargains: localhost:3000/bargains



<%= link_to 'index', :highlights %>
<%= link_to 'new', [:new, :highlight] %>
<%= link_to 'edit', [:edit, @ad] %>
<%= link_to 'destroy', @ad, :method => :delete %>

for being polymorphic :)

<%= link_to 'index', @ad.class %>

STI with a Single Controller

In that case you'd need to communicate the type to create to the controller somehow.

This is most easily established by including a hidden field in the form which has a value of the type you want to create.

Assuming your block variable for the form is f

<%= f.hidden_field :type, :value => "Excercise" %>

Then you can build a goal like this: params[:type])

You can easily see how this can be very dangerous, you should always be doubly careful when using user submitted data.

To guard against a malicious user, you could set a constant in your controller

AcceptedModels = ["Excercise", "Yoga", "Water"]

def accepted_type
(AcceptedModels & params[:type]).first

If you also want to hide your internal structure, you could use a hash and send any identifier

AcceptedModels = {"0": "Excercise", "1": "Yoga", "2": "Water"}

def accepted_type
AcceptedModels.fetch(params[:type], nil)

in either case you can then build your goal like this: accepted_type)

The biggest downside with this is that you will have to keep the AcceptedModels updated whenever you add/remove more goals

Rails single table inheritance. Shared controller, howto Update (CRUD)

There is indeed a much more elegant solution. Just change your form_for declaration and add the as option, like this:

<%= form_for(@user, as: :user, url: user_path(@user), method: :put) do |f| %>

That way in your controller your parameters will be scoped under the user key instead of the model's class.

STI and Controller

In my opinion the best way is to do it like this: (I am not sure if this is the best and more efficient way)

First. Add some new routes:

resources :cars, :controller => "vehicle", :type => "Car"
resources :buses, :controller => "vehicle", :type => "Bus"

Add a private method to your controller to convert your type param to the actual class constant you want to use:

def vehicle_type

Then in the controller actions you can do:

def new

def create
# ...

def index


<%= link_to 'index', :cars %>
<%= link_to 'new', [:new, :car] %>
<%= link_to 'edit', [:edit, @vehicle] %>
<%= link_to 'destroy', @vehicle, :method => :delete %>

for being polymorphic :)

<%= link_to 'index', @vehicle.class %>

PS: My answer was taken from and my experience

Related Topics

Leave a reply