aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations.rb
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-05-25 12:29:00 +0100
committerPratik Naik <pratiknaik@gmail.com>2008-05-25 12:29:00 +0100
commit98dc582742779081e71e697fcdf8d9ae2b421b16 (patch)
treef5680eef86e689a10d0f75434ba6a4e94829e439 /activerecord/lib/active_record/associations.rb
parent6277fd91133a3566333612857510d74de60d67f4 (diff)
downloadrails-98dc582742779081e71e697fcdf8d9ae2b421b16.tar.gz
rails-98dc582742779081e71e697fcdf8d9ae2b421b16.tar.bz2
rails-98dc582742779081e71e697fcdf8d9ae2b421b16.zip
Merge docrails.
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-xactiverecord/lib/active_record/associations.rb124
1 files changed, 67 insertions, 57 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 6c21fed8ea..a3d1bbbada 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -155,7 +155,7 @@ module ActiveRecord
#
# == Cardinality and associations
#
- # ActiveRecord associations can be used to describe one-to-one, one-to-many and many-to-many
+ # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
# relationships between models. Each model uses an association to describe its role in
# the relation. The +belongs_to+ association is always used in the model that has
# the foreign key.
@@ -441,9 +441,9 @@ module ActiveRecord
#
# == Eager loading of associations
#
- # Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is
+ # Eager loading is a way to find objects of a certain class and a number of named associations. This is
# one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
- # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example:
+ # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example:
#
# class Post < ActiveRecord::Base
# belongs_to :author
@@ -452,7 +452,7 @@ module ActiveRecord
#
# Consider the following loop using the class above:
#
- # for post in Post.find(:all)
+ # for post in Post.all
# puts "Post: " + post.title
# puts "Written by: " + post.author.name
# puts "Last comment on: " + post.comments.first.created_on
@@ -462,14 +462,15 @@ module ActiveRecord
#
# for post in Post.find(:all, :include => :author)
#
- # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol, so the find will now weave in a join something
- # like this: <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Doing so will cut down the number of queries from 201 to 101.
+ # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol. After loading the posts, find
+ # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102.
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
# for post in Post.find(:all, :include => [ :author, :comments ])
#
- # That'll add another join along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt>. And we'll be down to 1 query.
+ # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries
+ # will be 1 plus the number of associations named (except if some of the associations are polymorphic +belongs_to+ - see below).
#
# To include a deep hierarchy of associations, use a hash:
#
@@ -482,81 +483,91 @@ module ActiveRecord
# the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
# catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
#
- # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So
- # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the
- # <tt>:select</tt> option is ignored.
+ # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
+ # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
+ #
+ # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
#
- # You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions
- # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich
- # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading.
+ # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
+ # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
+ # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
+ # and not just to the association. You must disambiguate column references for this fallback to happen, for example
+ # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
+ #
+ # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association
+ # which has conditions defined on it:
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
+ # end
+ #
+ # Post.find(:all, :include => :approved_comments)
+ #
+ # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
#
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
#
- # Eager loading is not supported with polymorphic associations up to (and including)
- # version 2.0.2. Given
+ # Eager loading is supported with polymorphic associations.
#
# class Address < ActiveRecord::Base
# belongs_to :addressable, :polymorphic => true
# end
#
- # a call that tries to eager load the addressable model
- #
- # Address.find(:all, :include => :addressable) # INVALID
- #
- # will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent model's type
- # is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that early query.
+ # A call that tries to eager load the addressable model
#
- # In versions greater than 2.0.2 eager loading in polymorphic associations is supported
- # thanks to a change in the overall preloading strategy.
+ # Address.find(:all, :include => :addressable)
#
- # It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load
- # their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand.
+ # will execute one query to load the addresses and load the addressables with one query per addressable type.
+ # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
+ # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
+ # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
#
# == Table Aliasing
#
- # ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
+ # Active Record uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
# the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended
# for any more successive uses of the table name.
#
- # Post.find :all, :include => :comments
- # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ...
- # Post.find :all, :include => :special_comments # STI
- # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... AND comments.type = 'SpecialComment'
- # Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
- # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts
+ # Post.find :all, :joins => :comments
+ # # => SELECT ... FROM posts INNER JOIN comments ON ...
+ # Post.find :all, :joins => :special_comments # STI
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
+ # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
#
# Acts as tree example:
#
- # TreeMixin.find :all, :include => :children
- # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
- # TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes
- # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
- # LEFT OUTER JOIN parents_mixins ...
- # TreeMixin.find :all, :include => {:children => {:parent => :children}}
- # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
- # LEFT OUTER JOIN parents_mixins ...
- # LEFT OUTER JOIN mixins childrens_mixins_2
+ # TreeMixin.find :all, :joins => :children
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # TreeMixin.find :all, :joins => {:children => :parent}
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # INNER JOIN parents_mixins ...
+ # TreeMixin.find :all, :joins => {:children => {:parent => :children}}
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # INNER JOIN parents_mixins ...
+ # INNER JOIN mixins childrens_mixins_2
#
# Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
#
- # Post.find :all, :include => :categories
- # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
- # Post.find :all, :include => {:categories => :posts}
- # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
- # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
- # Post.find :all, :include => {:categories => {:posts => :categories}}
- # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
- # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
- # LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts
+ # Post.find :all, :joins => :categories
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # Post.find :all, :joins => {:categories => :posts}
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+ # Post.find :all, :joins => {:categories => {:posts => :categories}}
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+ # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
#
# If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
#
- # Post.find :all, :include => :comments, :joins => "inner join comments ..."
- # # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ...
- # Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..."
- # # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ...
- # LEFT OUTER JOIN comments special_comments_posts ...
+ # Post.find :all, :joins => :comments, :joins => "inner join comments ..."
+ # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
+ # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
+ # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
+ # INNER JOIN comments special_comments_posts ...
# INNER JOIN comments ...
#
# Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
@@ -842,7 +853,6 @@ module ActiveRecord
# this results in a counter with +NULL+ value, which will never increment.
# Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
# * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
- # Not allowed if the association is polymorphic.
# * <tt>:polymorphic</tt> - 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. <tt>class Post; attr_readonly :comments_count; end</tt>).
@@ -942,7 +952,7 @@ module ActiveRecord
#
# Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
# +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
- # +ReadOnly+ (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
+ # readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
# associations with attributes to a real join model (see introduction).
#
# Adds the following methods for retrieval and query: