Skip to content

Commit

Permalink
Improve Performance of calling create on has_many :through associatio…
Browse files Browse the repository at this point in the history
…ns by avoiding loading the target collection. Closes #8150 [evan]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6581 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
NZKoz committed Apr 26, 2007
1 parent baba45d commit 9d08a07
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 9 deletions.
2 changes: 2 additions & 0 deletions activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*

* Improve performance of calling .create on has_many :through associations. [evan]

* Improved cloning performance by relying less on exception raising #8159 [Blaine]

* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> [DHH]
Expand Down
Expand Up @@ -51,16 +51,14 @@ def <<(*records)
through = @reflection.through_reflection
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) if @owner.new_record?

load_target

klass = through.klass
klass.transaction do
flatten_deeper(records).each do |associate|
raise_on_type_mismatch(associate)
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record?

@owner.send(@reflection.through_reflection.name).proxy_target << klass.with_scope(:create => construct_join_attributes(associate)) { klass.create! }
@target << associate
@target << associate if loaded?
end
end

Expand Down Expand Up @@ -138,11 +136,11 @@ def construct_owner_attributes(reflection)

# Construct attributes for :through pointing to owner and associate.
def construct_join_attributes(associate)
returning construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) do |join_attributes|
if @reflection.options[:source_type]
join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
end
join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
if @reflection.options[:source_type]
join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
end
join_attributes
end

# Associate attributes pointing to owner, quoted.
Expand Down
16 changes: 14 additions & 2 deletions activerecord/test/associations_test.rb
Expand Up @@ -7,6 +7,7 @@
require 'fixtures/computer'
require 'fixtures/customer'
require 'fixtures/order'
require 'fixtures/categorization'
require 'fixtures/category'
require 'fixtures/post'
require 'fixtures/author'
Expand Down Expand Up @@ -69,7 +70,7 @@ def test_storing_in_pstore
end

class AssociationProxyTest < Test::Unit::TestCase
fixtures :authors, :posts
fixtures :authors, :posts, :categorizations, :categories

def test_proxy_accessors
welcome = posts(:welcome)
Expand All @@ -90,6 +91,17 @@ def test_proxy_accessors
assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
end

def test_push_does_not_load_target
david = authors(:david)
not_loaded_string = '<categories not loaded yet>'
not_loaded_re = Regexp.new(not_loaded_string)

david.categories << categories(:technology)
assert_match not_loaded_re, david.inspect
assert_equal not_loaded_string, david.categories.inspect
assert david.categories.include?(categories(:technology))
end

def test_inspect_does_not_load_target
david = authors(:david)
not_loaded_string = '<posts not loaded yet>'
Expand Down Expand Up @@ -660,7 +672,7 @@ def test_adding_before_save
assert_equal 2, new_firm.clients_of_firm.size
assert_equal 2, new_firm.clients_of_firm(true).size
end

def test_invalid_adding
firm = Firm.find(1)
assert !(firm.clients_of_firm << c = Client.new)
Expand Down

0 comments on commit 9d08a07

Please sign in to comment.