public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
explicitly including child associations that are also included in the parent 
association definition should not result in double records in the 
collection/double loads (#1110)

Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#1110 state:committed]
Will Bryant (author)
Fri Sep 26 20:28:21 -0700 2008
NZKoz (committer)
Fri Oct 10 07:58:39 -0700 2008
commit  4c05055487e149bfa4152c1b42f3519671ca22ac
tree    e48d836fa25a2c06544b792f4d72c0f7051be9ee
parent  28393e6e9c9368036e65e77175ea4f65a862259c
...
193
194
195
 
196
197
198
...
214
215
216
 
217
218
219
...
271
272
273
 
274
275
276
...
193
194
195
196
197
198
199
...
215
216
217
218
219
220
221
...
273
274
275
276
277
278
279
0
@@ -193,6 +193,7 @@ module ActiveRecord
0
       end
0
 
0
       def preload_has_one_association(records, reflection, preload_options={})
0
+        return if records.first.send("loaded_#{reflection.name}?")
0
         id_to_record_map, ids = construct_id_map(records)        
0
         options = reflection.options
0
         records.each {|record| record.send("set_#{reflection.name}_target", nil)}
0
@@ -214,6 +215,7 @@ module ActiveRecord
0
       end
0
 
0
       def preload_has_many_association(records, reflection, preload_options={})
0
+        return if records.first.send(reflection.name).loaded?
0
         options = reflection.options
0
 
0
         primary_key_name = reflection.through_reflection_primary_key_name
0
@@ -271,6 +273,7 @@ module ActiveRecord
0
       end
0
 
0
       def preload_belongs_to_association(records, reflection, preload_options={})
0
+        return if records.first.send("loaded_#{reflection.name}?")
0
         options = reflection.options
0
         primary_key_name = reflection.primary_key_name
0
 
...
1248
1249
1250
 
 
 
 
 
1251
1252
1253
...
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
0
@@ -1248,6 +1248,11 @@ module ActiveRecord
0
             association.target.nil? ? nil : association
0
           end
0
 
0
+          define_method("loaded_#{reflection.name}?") do
0
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
0
+            association && association.loaded?
0
+          end
0
+
0
           define_method("#{reflection.name}=") do |new_value|
0
             association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
0
 
...
18
19
20
21
 
22
23
24
...
111
112
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
115
116
...
18
19
20
 
21
22
23
24
...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
0
@@ -18,7 +18,7 @@ require 'models/developer'
0
 require 'models/project'
0
 
0
 class EagerAssociationTest < ActiveRecord::TestCase
0
-  fixtures :posts, :comments, :authors, :categories, :categories_posts,
0
+  fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts,
0
             :companies, :accounts, :tags, :taggings, :people, :readers,
0
             :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
0
             :developers, :projects, :developers_projects
0
@@ -111,6 +111,46 @@ class EagerAssociationTest < ActiveRecord::TestCase
0
     end
0
   end
0
 
0
+  def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
0
+    author_id = authors(:david).id
0
+    author = assert_queries(3) { Author.find(author_id, :include => {:posts_with_comments => :comments}) } # find the author, then find the posts, then find the comments
0
+    author.posts_with_comments.each do |post_with_comments|
0
+      assert_equal post_with_comments.comments.length, post_with_comments.comments.count
0
+      assert_equal nil, post_with_comments.comments.uniq!
0
+    end
0
+  end
0
+
0
+  def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once
0
+    author = authors(:david)
0
+    post = author.post_about_thinking_with_last_comment
0
+    last_comment = post.last_comment
0
+    author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
0
+    assert_no_queries do
0
+      assert_equal post, author.post_about_thinking_with_last_comment
0
+      assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
0
+    end
0
+  end
0
+
0
+  def test_finding_with_includes_on_belongs_to_association_with_same_include_includes_only_once
0
+    post = posts(:welcome)
0
+    author = post.author
0
+    author_address = author.author_address
0
+    post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
0
+    assert_no_queries do
0
+      assert_equal author, post.author_with_address
0
+      assert_equal author_address, post.author_with_address.author_address
0
+    end
0
+  end
0
+
0
+  def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
0
+    post = posts(:welcome)
0
+    post.update_attributes!(:author => nil)
0
+    post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
0
+    assert_no_queries do
0
+      assert_equal nil, post.author_with_address
0
+    end
0
+  end
0
+
0
   def test_loading_from_an_association
0
     posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
0
     assert_equal 2, posts.first.comments.size
...
17
18
19
 
 
20
21
22
...
17
18
19
20
21
22
23
24
0
@@ -17,6 +17,8 @@ class Author < ActiveRecord::Base
0
       proxy_target
0
     end
0
   end
0
+  has_one  :post_about_thinking, :class_name => 'Post', :conditions => "posts.title like '%thinking%'"
0
+  has_one  :post_about_thinking_with_last_comment, :class_name => 'Post', :conditions => "posts.title like '%thinking%'", :include => :last_comment
0
   has_many :comments, :through => :posts
0
   has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
0
   has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
...
13
14
15
 
16
17
18
...
13
14
15
16
17
18
19
0
@@ -13,6 +13,7 @@ class Post < ActiveRecord::Base
0
   end
0
 
0
   belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
0
+  belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
0
 
0
   has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
0
 

Comments