Friday, January 11, 2008

STI Experiment 1 - Scaffolded STI Sub-Classes

I was having problem listing STI related objects from the same table.
Here are my experiment with trying to work through the problems.

My first experiment will try to create 2 completely scaffolded models, create a simple STI hierarchy between them and see how Rails handles it.

At the command line:

ruby script\generate scaffold Parent name:string description:text
rake db:migrate
ruby script\generate scaffold Son member:string
Make 2 changes to the generated code:
  1. Change the migration from creating a new sons table to adding 2 new columns to the parents table:
    • add_column :parents, :member, :string
    • add_column :parents, :type, :string # needed for STI
  2. Make Son (in app/models/son.rb) derive from Parent (instead of ActiveRecord::Base)
rake db:migrate again, and add a few Parents and a Son (through the console).

Result
At URL /parents all Parents and Sons are listed, since they are all saved in the same table.
This is the desired behavior which would be expected with an is-a relationship between Sons and Parents. (Note that I use parent and son names in the parent i.e. base class and son i.e. derived class meaning and not in the genealogical sense)
This view is generated by the scaffold generator script, and is actually app\views\parents\index.html.erb.

Since Sons are derived but different types from Parents it might be expected that the Show, Edit and Destroy links for actual concrete Sons will link to the (automatically generated) Son controller and the not to the Parent controller. This would essentially be equivalent to calling virtual methods.

This actually happens for the Show and Destroy links. However, the Edit link still redirects to the Parent edit page. The problem apparently stems from the Parent index view:
<%= link_to 'Show', parent %>
<%= link_to 'Edit', edit_parent_path(parent) %>
<%= link_to 'Destroy', parent, :confirm => 'Are you sure?', :method => :delete %>
the edit_parent_path(parent) seems to be not as smart as link_to in detecting the STI hierarchy on the parent object.

I don't know how to bypass this at the moment.
I will update this post if and when I have a working solution.

If you found this post helpful, or you can shed some light on this problem, please leave a message in the comments. If you want to read more about my exploration of polymorphism and STI on Rails, subscribe to the blog feed and stay updated.

4 comments:

Håkon Bommen said...

This was just what I was looking for!

I switched to STI, and whoops everything worked fine except editing. Now I've created a minimal controller for each child with edit and update actions, redirecting the rest to the parent models controller. It's still a lot of code to get it working again.

Håkon Bommen said...

Problem solved.

Problem description at: http://dev.rubyonrails.org/ticket/10454

In my project Employee extends User. In user controller, I replaced

params[:user] with params[@user.class.name.underscore]

and

redirect_to(@user) with
redirect_to(:controller => 'users', :action => 'show', :id => @user.id)

Håkon Bommen said...

.....and specify the controller in view/user/edit.html.erb

form_for( @user, :url => {:controller => 'users', :action => :update} )

and

link_to 'Show', user_path(@user)

Unknown said...

Perfeito, tava de cabeça quente já com esse edit. Funcionou perfeitamente. Valew Håkon