stevengharms.com

Sententiae viri ex temporibus duobus

[Rails] Associations

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.

If, when getting your object model to work in Rails seems really hard and sorta backwards, you should say “this seems needlessly hard, maybe I’m doing it the wrong way” and bail out of that design. The LOLCatz rules apply here: “Using Rails to make your life easy - ur doin' it wrong

Comments