Michael D.
Michael D.

Reputation: 399

has_many polymorphism/inheritance in Rails

I am trying to set up a polymorphic relationship in Rails and have been running into some difficulty. Here's my data model:

class Order
    has_many :order_items
end

class OrderItem
    belongs_to :order
end

class PhysicalItem < OrderItem
end

class VirtualItem < OrderItem
end

PhysicalItem and VirtualItem have enough differences in their model to warrant being split out into their own tables. So, I envision there being:

an orders table

a physical_items table

a virtual_items table

an order_items table with item_type = ["PhysicalItem" or "VirtualItem"] and item_id of the matching row in the corresponding table.

I eventually want to be able to write code like this:

order = Order.new
physical_item = PhysicalItem.new
virtual_item = VirtualItem.new

order.order_items << physical_item
order.order_items << virtual_item

puts order.order_items
# Should list out the physical item and then the virtual item.

It seems very simple in theory, but it doesn't look like there's much support for this structure in general. Anyone have any thoughts about implementing this in a postgresql database with ActiveRecord?

Upvotes: 1

Views: 342

Answers (2)

bennick
bennick

Reputation: 1917

You could use the PostgreSQL HStore datatype in conjunction with STI. This way you gain the benefit of running a single SELECT on one table but that table is not polluted with type specific columns.

You will then have only two tables:

  • orders table
  • order_items table

For the order_items table the migration would consist of:

class CreateOrderItemsTable < ActiveRecord::Migration
  def change
    create_table :order_items do |t|
      t.integer :order_id
      t.string :type
      t.hstore :data
    end
  end
end

Your models would then look like this:

class Order
  has_many :order_items
end

class OrderItem
  belongs_to :order
  serialize :data, ActiveRecord::Coders::Hstore
end

class PhysicalItem < OrderItem
end

class VirtualItem < OrderItem
end

I tried using a polymorphic join table but it still required too many SQL quires just to get a list of the associations. HStore with STI is a great combination. For more information read this: http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination

Upvotes: 0

jvperrin
jvperrin

Reputation: 3368

You shouldn't need polymorphic associations for this. A method in the Order model would work just as well:

class Order < ActiveRecord::Base
  has_many :physical_items
  has_many :virtual_items

  def order_items
    physical_items + virtual_items
  end
end

class PhysicalItem < ActiveRecord::Base
  belongs_to :order
end

class VirtualItem < ActiveRecord::Base
  belongs_to :order
end

You will also need the physical_items and virtual_items tables to both have order_id columns. Then, to replicate the behavior you wanted:

order = Order.new
physical_item = order.physical_items.new
virtual_item = order.virtual_items.new

puts order.order_items

Upvotes: 1

Related Questions