LRBlog

Logical Reality Design: Web Design and Software Development

Single Table Inheritance and RESTful Routes

March 17, 2009

I'm converting an old, controller/action/id style Rails application to a more RESTful way of doing things, and ran into a brief roadblock: one of my main tables uses single table inheritance to generate three subclasses of items. I never actually use the superclass "task", I only use the three subclasses "action item", "work order", and "problem report".

So, I ran into this little challenge: all three STI subclasses use the same controller, "tasks", because they all have essentially the same behavior and differ only in minor details. But, when I do a resources map:

map.resources :tasks

Then I get errors in much of my code when I say things like redirect_to @task, because if that task happens to be an ActionItem, it's trying to call action_item_path(@task), which doesn't exist.

I googled around a bit to no result. Striking out on my own, it turns out the answer is as simple as mapping each resource independently, and just overriding the controller in map.resources:

In config/routes.rb

map.resources :tasks
map.resources :action_items, :controller => 'tasks'
map.resources :work_orders, :controller => 'tasks'
map.resources :problem_reports, :controller => 'tasks'

Now, redirect_to @task works just fine regardless of which subclass @task happens to be.

  1. Josh Illian Said,

    I was having this exact same problem with routing for a single-table inheritance-based hierarchy of models and this worked like a charm.

    Thanks!

  2. Evan Said,

    Josh: glad to have helped.

  3. Matt Said,

    Brilliant. Great post, that was really bugging me. Thanks Evan.

  4. Fretta Said,

    Great post! Was about to start pulling my hair. Thanks Evan.

  5. peter Said,

    Thanks for this one. Exact same situation for me. Knew there had to be something better than what I was originally up to.

  6. Javix Said,

    One more question, – how to distinguish actionitems from others (work_orders, problems_reports) in your controller (new, create, edit, update) and how to create the views? Should I create a seperate folder for every subclass with coresponding views (new, edit, etc.) ?Thanks.

  7. Javix Said,

    sorry for the typo in my mail address

  8. Evan Said,

    Javix: The primary reason I do things this way is because, for most purposes, I don’t want to distinguish the different types of tasks in the controller. They’re nearly identical and I want to use the same code. The whole point of the exercise is that ActionItems, WorkOrders etc. all use the same controller action and views so I don’t have to write them twice.

    For the few things that need to be different — for example, ProblemReports have one fewer column in the table view than AIs or WOs — I can have my views or controllers check @task.type. For example, in app/views/tasks/show.html.erb I might have this line:


    < %- unless @task.type == 'problem_report' %>

    < %= h @task.some_field_that_problem_reports_dont_use %>

    < % end %>

  9. Chris Said,

    Another thank you – I ran into the same situation converting an old app, and you saved me a lot of time!

  10. Claudio Said,

    Thank you Evan your solution is very helpful for me!

  11. Casper Said,

    Thank you, this saved me some time.

  12. Mark S Said,

    I achieved this globally by overriding the following :

    module ActionController
    module RecordIdentifier
    private
    def model_name_from_record_or_class(record_or_class)
    (record_or_class.is_a?(Class) ? record_or_class.base_class : record_or_class.class.base_class).model_name
    end
    end
    end

    The same can be achieved only for specific STI cases with :

    @subclassRecord.becomes(BaseClassName)

    This saves having extra routes to trawl through and so forth.

  13. Jimmy Said,

    Wish i found this post about two hours ago, thanks!

  14. STI breaks routes in rails « Clipboard Said,

    […] the blog post Single Table Inheritance and RESTful Routes at Logical Reality Design, I see the method that Alex Reisner advised against, viz. adding […]

Add A Comment