Flowdock
belongs_to(association_id, options = {}) public

Adds the following methods for retrieval and query for a single associated object for which this object holds an id: association is replaced with the symbol passed as the first argument, so belongs_to :author would add among others author.nil?.

  • association(force_reload = false) - returns the associated object. nil is returned if none is found.
  • association=(associate) - assigns the associate object, extracts the primary key, and sets it as the foreign key.
  • association.nil? - returns true if there is no associated object.
  • build_association(attributes = {}) - returns a new object of the associated type that has been instantiated with attributes and linked to this object through a foreign key, but has not yet been saved.
  • create_association(attributes = {}) - returns a new object of the associated type that has been instantiated with attributes, linked to this object through a foreign key, and that has already been saved (if it passed the validation).

Example: A Post class declares belongs_to :author, which will add:

  • Post#author (similar to Author.find(author_id))
  • Post#author=(author) (similar to post.author_id = author.id)
  • Post#author? (similar to post.author == some_author)
  • Post#author.nil?
  • Post#build_author (similar to post.author = Author.new)
  • Post#create_author (similar to post.author = Author.new; post.author.save; post.author)

The declaration can also include an options hash to specialize the behavior of the association.

Options are:

  • :class_name - specify the class name of the association. Use it only if that name can’t be inferred from the association name. So has_one :author will by default be linked to the Author class, but if the real class name is Person, you’ll have to specify it with this option.
  • :conditions - specify the conditions that the associated object must meet in order to be included as a WHERE SQL fragment, such as authorized = 1.
  • :order - specify the order in which the associated objects are returned as an ORDER BY SQL fragment, such as last_name, first_name DESC
  • :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name of the association with an _id suffix. So a class that defines a +belongs_to :person+ association will use person_id as the default foreign_key. Similarly, +belongs_to :favorite_person, :class_name => "Person"+ will use a foreign key of favorite_person_id.
  • :counter_cache - caches the number of belonging objects on the associate class through the use of increment_counter and decrement_counter. The counter cache is incremented when an object of this class is created and decremented when it’s destroyed. This requires that a column named #{table_name}_count (such as comments_count for a belonging Comment class) is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing a column name instead of a true/false value to this option (e.g., :counter_cache => :my_custom_counter.) Note: Specifying a counter_cache will add it to that model’s list of readonly attributes using #attr_readonly.
  • :include - specify second-order associations that should be eager loaded when this object is loaded. Not allowed if the association is polymorphic.
  • :polymorphic - specify this association is a polymorphic association by passing true. Note: If you’ve enabled the counter cache, then you may want to add the counter cache attribute to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).

Option examples:

  belongs_to :firm, :foreign_key => "client_of"
  belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
  belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
             :conditions => 'discounts > #{payments_count}'
  belongs_to :attachable, :polymorphic => true
Show source
Register or log in to add new notes.
August 25, 2010 - (>= v2.3.8)
9 thanks

Undocumented :inverse_of option

Support for the :inverse_of option was backported to 2.3.6+.

Here’s the description from the original commit: http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237


You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:

class Man < ActiveRecord::Base
  has_one :face, :inverse_of => :man
end

class Face < ActiveRecord::Base
  belongs_to :man, :inverse_of => :face
end

m = Man.first
f = m.face

Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance.

Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it’s not a polymorphic.

October 30, 2008
7 thanks

Can be extended but only with a module

Although not documented, belongs_to does support Association Extensions however it doesn’t accept a block like has_many does. So you can’t do this:

class Account < ActiveRecord::Base
  belongs_to :person do
    def do_something_funky
      # Some exciting code
    end
  end
end

but you can do this:

module FunkyExtension
  def do_something_funky
    # Some exciting code
  end
end

class Account < ActiveRecord::Base
  belongs_to :person, :extend => FunkyExtension
end

And then call it like this:

@account = Account.first
@account.person.do_something_funky
February 27, 2009
6 thanks

Extend with an anonymous module

You can extend with an anonymous module for one-off cases that won’t be repeated:

belongs_to :container, :polymorphic => true, :extend => ( Module.new do
    def find_target
      ...
    end
  end )

The parentheses are important, will fail silently without them.

February 23, 2010
3 thanks

Easy workaround for missing :through option

Note that belongs_to does not support :through option like has_many (although IMHO it would make sense in some cases), but you can easily simulate it with delegate.

For example:

class Person < ActiveRecord::Base
  belongs_to :team
  ...
end
class Task < ActiveRecord::Base
  belongs_to :person
  delegate :team, :to => :person
end

There is of course more ways to do it, but this seems to be the easiest to me.

April 2, 2009
2 thanks

Exension module patch

I’d say its just an oversight.

If you’d like to see all associations get equal support for extension modules take a look at this patch and give it a +1.

February 25, 2014
1 thank

missing :through option

So the way to do the equivalent of a has_many :through is to use has_one :through, with the expected names.

so using the other example we could do

eg

class Person < ActiveRecord::Base
  belongs_to :team
  ...
end
class Task < ActiveRecord::Base
  belongs_to :person
  has_one :team, :through => :person
end
February 9, 2009
1 thank

:foreign_type option

I’m not sure if this has always been around but in 2.3, belongs_to takes a :foreign_type option on polymorphic associations. This behaves the same way as :foreign_key but for the type field.

April 30, 2009
1 thank

Caveat and design hints regarding :counter_cache

(From Obie Fernandez/ The Rails Way, ISBN 978-0321445612. Thanks Obie!)

This caveat:

The value of the counter cache column must be set to zero by default in the database! Otherwise the counter caching won’t work at all. It’s because the way that Rails implements the counter caching behavior is by adding a simple callback that goes directly to the database with an UPDATE command and increments the value of the counter.

And these tips:

If a significant percentage of your association collections will be empty at any given moment, you can optimize performance at the cost of some extra database storage by using counter caches liberally. The reason is that when the counter cache attribute is at zero, Rails won’t even try to query the database for the associated records!

If you’re not careful, and neglect to set a default value of 0 for the counter cache column on the database, or misspell the column name, the counter cache will still seem to work! There is a magic method on all classes with has_many associations called collection_count, just like the counter cache. It will return a correct count value if you don’t have a counter cache option set or the counter cache column value is null!

April 2, 2009
1 thank

Patch looks good.

I assume commenting right below the ticket is the “+1” action? :) Thanks for the reply and patch efforts!

April 3, 2010
0 thanks

:autosave => false vs. :autosave => nil

The documentation above mentions that :autosave => true always saves the association and that it’s “off” by default. What it doesn’t mention what they mean by “off”.

  • :autosave => nil (the default “off” behavior) will still autosave the association if it has changed or is a new record.

  • :autosave => false seems to prevent autosaving of the association, even if it has changed.

I’ve found :autosave => false to be useful behavior when trying to prevent cyclical dependency loops; there are likely other useful use cases out there.

November 30, 2012 - (v3.2.3)
0 thanks

Can't find documention on :find_by option

I found code that had a :find_by option on belongs_to. I’m sure it’s more or less self explanatory, but I couldn’t find it listed anywhere as an option.

My bad, belongs_to was in a controller, not a model.

April 2, 2009
0 thanks

Why such inconsistency on 'extend' design?

Thanks for the great note! Finally… it took me quite some time to find this page and track down why there is such inconsistency between belongs_to and, say, has_many, proxy extension design. Do you (or anyone knowledgeable here) know the reason behind such design inconsistency? It’s quite annoying (and quite abstraction- and documentation-defeating) that one has to look this deep into the source code to see what’s going on.… Thanks!

June 18, 2010
0 thanks

Careful when updating foreign key directly

Seems when you change key directly it doesn’t update association automatically.

>> chicken = Chicken.first
>> chicken.head
=> old_head
>> chicken.head_id = new_head.id
>> chicken.head
=> old_head

Easy (stupid?) way to fix it:

class Chicken
  def head_id=(value)
    self.head = Head.find_by_id(value)
  end
end