Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.

for the record

Ok, I just want to set this one in stone.

Rails’ polymorphic associations.

Ask yourself 2 questions:
  1. Can this object belong to more than 1 type of object
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    class Car < ActiveRecord::Base
    end
    
    class House < ActiveRecord::Base
    end
    
    class LineItem < ActiveRecord::Base
    
      belongs_to :salable, :polymorphic => true
    
    end
    Here’s a domain model from an app in which you can purchase both cars and houses. They are both salable products that will appear on a line item in an order. This is a many-to-1 relationship from a LineItem to its salable product.
  2. Can this object belong to more than 1 type of object and more than 1 object
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    class User < ActiveRecord::Base
    
      include Groupable
    
    end
    
    class Account < ActiveRecord::Base
    
      include Groupable
    
    end
    
    class Membership < ActiveRecord::Base
    
      belongs_to :groupable, :polymorphic => true
      belongs_to :group
    
    end
    
    class Group < ActiveRecord::Base
    
      has_many :memberships
    
    end
    
    module Groupable
    
     def self.included(clazz)
        clazz.class_eval
          has_many :memberships, :as => :groupable
        end
      end
    
    end

    Here we have a many-to-many between groupable objects and groups. Since a Group can contain members of different types it needs to be polymorphic. However, in this example a single group can have multiple members all of different types. Like any many-to-many we need a table between the 2 other tables. Since habtm doesn’t support polymorphic associations we have to go with a join model. So we introduced Membership and made it have a polymorphic relationship.

    It’d be sweet if we didnt need this join model because I can’t stand unnecessary classes, something like:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    class User < ActiveRecord::Base
    
      include Groupable
    
    end
    
    class Account < ActiveRecord::Base
    
      include Groupable
    
    end
    
    class Group < ActiveRecord::Base
    
      has_and_belongs_to_many :groupables
    
    end
    
    module Groupable
    
      def self.included(clazz)
        clazz.class_eval
          has_and_belongs_to_many :groups, :as => :groupable
        end
      end
    
    end

    db schema:

    
      groupables (groupable_id, groupable_type, group_id)
    
    
    That’s some made up syntax. I’ll say if you have an :as parameter to #has_and_belongs_to_many then Rails will look for a table named after the polymorphic interface, in this case groupables, and 2 columns groupable_id and groupable_type. Since the #has_and_belongs_to_many call in the Group model doesn’t include an :as parameter, Rails will, like in a normal #has_and_belongs_to_many call, look for a table named after the association, in this case groupables, and a foreign key in that table referencing this model, in this case group_id.

About this entry

 

thoughtbot is hiring

We are hiring web developers and web designers in both Boston and New York, NY.

What are we up to?

We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.


Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.

About thoughtbot, inc.

We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.