RANT: belongs_to -> refers_to

Every once in a while, I pipe up and whine that “belongs_to” should
really
be 'refers_to" [http://dev.rubyonrails.org/ticket/2130]. It’s time
again.

I’ve got a bunch of stuff going onto eBay, so, like any good engineer, I
don’t just post it manually: I design a Rails-based inventory database
that
creates a semantically-correct XHTML/CSS auction posting with a variable
number of thumbnails (stored with file_column) and Textile fields.

I start out with

class Item < ActiveRecord::Base
:has_many :pictures
end class

But I want one of those pictures to be featured as the gallery photo.
So,
naturally, I gravitate towards adding

class Item < ActiveRecord::Base
:has_one primary_picture, :class_name => ‘Picture’
end

I am encouraged in this futile pursuit by the docs, which I quickly skim
to
see things like

:has_one :last_comment, :class_name => “Comment”, :order =>
“posted_on”

Of course, in the light of day, it’s clear that this automagically
creates
the last_comment association by picking the most recent comment. But at
3am, it just reinforced the tendency to use has_one. Ditto the many
other
has_one examples I found - half of which used the automatic-first-record
feature, and half of which were probably just making the same mistake I
was.

I started getting nil objects, and was mystified until I finally tried
explicitly setting the foreign key:

:has_one primary_picture, :class_name => ‘Picture’,
:foreign_key => ‘primary_picture_id’

at which point Rails helpfully told me that there was no
Picture.primary_picture_id field. Then I remembered for the umpteenth
time
that has_one is for the record that does NOT have the foreign key, and
belongs_to is for the one that does, and I should not be misled by the
semantics of the actual has_one and belongs_to keywords.

Item :belongs_to primary_picture makes no sense. Item :refers_to
primary_picture does. This is way too small for even a plugin - and I
don’t think that type of syntax change belongs in a plugin, anyway. I
have
yet to hear anyone who’s opposed to it… can’t we PLEASE add this alias
and consider deprecating belongs_to over time? It’s… just… wrong.

Jay L.

On Sun, Apr 02, 2006 at 11:12:47AM -0400, Jay L. wrote:
} Every once in a while, I pipe up and whine that “belongs_to” should
really
} be 'refers_to" [http://dev.rubyonrails.org/ticket/2130]. It’s time
again.
[…]
} Item :belongs_to primary_picture makes no sense. Item :refers_to
} primary_picture does. This is way too small for even a plugin - and I
} don’t think that type of syntax change belongs in a plugin, anyway. I
have
} yet to hear anyone who’s opposed to it… can’t we PLEASE add this
alias
} and consider deprecating belongs_to over time? It’s… just… wrong.

In some contexts it is wrong. In others it is right, such as has_many.
Perhaps this would make your life easier:

class ActiveRecord::Base
class << self
alias :refers_to :belongs_to
end
end

} Jay L.
–Greg

If you really want this, just place this in your environment.rb

module ActiveRecord::Associations::ClassMethods
alias :refers_to :belongs_to
end

And there you have your refers_to method.

I’m opposed to it. I’m not sure if your problem is with grammar or
with the visual abstraction of what you are trying to do, but the
semantics are correct. Think of it this way:

Let’s say I have 50 widgets that I want to sell on eBay. Each of
these widgets will have its own auction posting. However, all the
widgets look alike, so I have a single picture of the widget. I
don’t want to have 50 pictures of my widget, so it would be incorrect
to say that each widget has_one picture. Instead, each widget
belongs_to that picture of a widget.

Now, you are using the term Item, so perhaps you have another model
to handle postings of the Item on eBay. This would change the
argument above slightly, but the semantics remain the same. If a
picture is used on only a single Item, but an item might have
multiple pictures (which is how I interpret this domain), then the
foreign key should be on the picture, not on the Item, and then you
would be correctly using the has_one and has_many.

Now, by placing the foreign key on the picture, you can achieve the
concept of a primary picture by placing a boolean flag on the picture
tuple akin to IsPrimary. Then set up your item model with a has_many
picture and a has_one primary picture (with the constraint that it
must have the IsPrimary flag set).

OR… you can have a has_may :pictures, belongs_to :primary_picture

OR… you can use acts_as_list on your has_many and maintain that the
first item in the list is the primary picture. I kinda like this
approach.

-Derrick S.

Improve your code

class Picture < AR:B
acts_as_list
end

class Item < ActiveRecord::Base
has_one :primary_picture, :class_name => ‘Picture’, :order =>
‘position ASC’
end

On 4/2/06, Jay L. [email protected] wrote:

class Item < ActiveRecord::Base
I am encouraged in this futile pursuit by the docs, which I quickly skim to

semantics of the actual has_one and belongs_to keywords.
Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


Tobi
http://shopify.com - modern e-commerce software
http://typo.leetsoft.com - Open source weblog engine
http://blog.leetsoft.com - Technical weblog

On Sun, 2 Apr 2006 12:51:54 -0400, Gregory S. wrote:

In some contexts it is wrong. In others it is right, such as has_many.
Perhaps this would make your life easier:

class ActiveRecord::Base
class << self
alias :refers_to :belongs_to
end
end

Yep, I keep swearing I’m gonna do that. I just think it might help
other
programmers as well - I often see belongs_to confusion here, especially
among novices.

Jay

On Sun, 2 Apr 2006 12:50:22 -0400, Derrick S. wrote:

Now, you are using the term Item, so perhaps you have another model
to handle postings of the Item on eBay. This would change the
argument above slightly, but the semantics remain the same. If a
picture is used on only a single Item, but an item might have
multiple pictures (which is how I interpret this domain), then the
foreign key should be on the picture, not on the Item, and then you
would be correctly using the has_one and has_many.

Your second guess is right - I’m not actually posting to eBay with this
model (yet), and the items are one-of-a-kind, so there’s no habtm
relationship; each picture is associated with one and only one item, but
each item has_many pictures.

Now, by placing the foreign key on the picture, you can achieve the
concept of a primary picture by placing a boolean flag on the picture
tuple akin to IsPrimary. Then set up your item model with a has_many
picture and a has_one primary picture (with the constraint that it
must have the IsPrimary flag set).

The problem with that is that there’s no way to enforce “one and only
one
primary picture” if the boolean’s in the picture.

OR… you can have a has_may :pictures, belongs_to :primary_picture

Which is what I’m doing now, but semantically, in this context, an item
doesn’t belong to a picture - it’s the other way around. Don’t you
think?

OR… you can use acts_as_list on your has_many and maintain that the
first item in the list is the primary picture. I kinda like this
approach.

Yep, I hadn’t thought of that, but it makes the most sense, and probably
would have fallen out once I started thinking about being able to
re-order
the pictures! That one’s nice.

Jay

Jay,

Just wanted to say that I pretty much agree with you. I was in
basically the exact same situation you were the other day, with a
listing of Items where each Item has_many :images, and I thought I could
do “has_one :main_image”, like you described. Of course I realized the
same thing you did and it still feels kinda funny, since semantically,
my Item doesn’t belong to its main picture, the picture belongs to the
item.

They even take time in Agile Web Dev to say “we know it seems funny to
say ‘belongs_to’, so think of it as ‘refers_to’”. Not sure I understand
why it was coded that way in the first place, since so many of Rails
conventions are based on easily translating coding semantics into
natural language semantics.

Jeff