POSTS

[Rails] Associations

Blog

Yesterday I found out I had made a major boo-boo in setting up an ActiveRecord::Association.

Imagine a “Widget” model that is composited of serveral other fields. You might think to yourself.

“An Widget has a SKU ( housed in table ‘skus’ based on model SKU ), a widget has a color, etc.”

You then might model your code such that

	Widget < ActiveRecord::Base
	  has_one :sku
	  has_one :color
	end

And accordingly

	SKU < ActiveRecord::Base
	  belongs_to :widget
	end

This is wrong.

You must think in terms of the most “atomic” element. What’s the relationship of a SKU? A SKU “has one” product to which it is applicable. You see, what you’ve done above is absolutely reverse of the way it should be.

Think this way:

	SKU < ActiveRecord::Base
	  has_one :widget
	end

And then just add the mirror entry, for completeness

	Widget < ActiveRecord::Base
	  belongs_to :sku
	end

You can see this explained in the Rails API:

class Employee < ActiveRecord::Base
    has_one :office
end

class Office < ActiveRecord::Base
   belongs_to :employee    # foreign key - employee_id
end

If you find yourself using the primary key of the composite entity to find something in the the constituent table or getting into weird sorts of problems with .create() or find yourself thinking that you may need to implement something like (although it is pretty cool):

	$ irb
	irb(main):001:0> class K
	irb(main):002:1> end
	=> nil
	irb(main):003:0> k1 = K.new
	=> #<k:0xb7fc1788>
	irb(main):004:0> k2 = eval('K').new
	=> #<k:0xb7fb8094>
	irb(main):005:0> k3 = Object.const_get('K').new
	=> #<k:0xb7fb1474>

[Reference]

The real difficulty here is with how English speakers construe the transitivity of the phrase “has one”. Does this mean:

  • X is a superior containing entity i.e. “contains” (“The happy meal has one hamburger and one toy”)
  • X is uniquely assigned in a relationship (“The parolee has one parole officer”)

The word “relationship” in the second bullet is the use of “has_one” that Rails uses the phrase “has_one” to signify.