Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix has_many assocation w/select load after create #7859

Merged
merged 1 commit into from

3 participants

@ernie

If you create a new record via a collection association proxy that has
not loaded its target, and which selects additional attributes through
the association, then when the proxy loads its target, it will
inadvertently trigger an ActiveModel::MissingAttributeError during
attribute writing when CollectionAssociation#merge_target_lists attempts
to do its thing, since the newly loaded records will possess attributes
the created record does not.

This error also raises a bogus/confusing deprecation warning when
accessing the association in Rails 3.2.x, so cherry-pick would be
appreciated!

@ernie ernie Fix has_many assocation w/select load after create
If you create a new record via a collection association proxy that has
not loaded its target, and which selects additional attributes through
the association, then when the proxy loads its target, it will
inadvertently trigger an ActiveModel::MissingAttributeError during
attribute writing when CollectionAssociation#merge_target_lists attempts
to do its thing, since the newly loaded records will possess attributes
the created record does not.

This error also raises a bogus/confusing deprecation warning when
accessing the association in Rails 3.2.x, so cherry-pick would be
appreciated!
9f3b8cd
@ernie

Quick note: an alternative (but slightly more cumbersome to implement) fix might be to detect those loaded records which possess attributes the in-memory versions don't have, and swap the attribute replacement out to the changes from the in-memory version. I'm working on that fix now.

@ernie

On second thought, no I'm not. That has some pretty serious drawbacks, in that the object would no longer be the same instance as the one that the user may have received as a return from the call to create.

@rafaelfranca
Owner

@tenderlove could you take a look?

@tenderlove
Owner
@tenderlove tenderlove was assigned
@ernie

Aloha, @tenderlove! I've been thinking about this and as much as I really wish there was a good way for an in-memory record to have identical attributes to those read through the association, the use case would be sufficiently rare that the solution here is probably "good enough."

Internal AR calls shouldn't be raising errors/deprecations (depending on version) that were intended to reflect changes being necessary to user code, so this patch avoids that. Swapping out the in-memory object for its from-disk counterpart would be bad for the reasons I mentioned the other day, and trying to step around the write_attribute protection against creating new attributes on an existing object (to use the in-memory version but graft on the additional attributes) is painful enough that I think the code is trying to tell us we'd be nuts to do it.

@tenderlove
Owner

@ernie ok! I'll merge as-is then! Thanks.

@tenderlove tenderlove merged commit 269adae into from
@ernie

@tenderlove Thanks! Any chance it could get cherry-picked to 3-2-stable as well? It doesn't raise an error there, but generates a bogus deprecation message:

DEPRECATION WARNING: You're trying to create an attribute `some_attribute'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc. (called from some_method at /some_file:5)
@ernie

Or, if you want, I can work out a test for 3-2-stable and submit a separate PR

@rafaelfranca

@ernie please submit a pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 5, 2012
  1. @ernie

    Fix has_many assocation w/select load after create

    ernie authored
    If you create a new record via a collection association proxy that has
    not loaded its target, and which selects additional attributes through
    the association, then when the proxy loads its target, it will
    inadvertently trigger an ActiveModel::MissingAttributeError during
    attribute writing when CollectionAssociation#merge_target_lists attempts
    to do its thing, since the newly loaded records will possess attributes
    the created record does not.
    
    This error also raises a bogus/confusing deprecation warning when
    accessing the association in Rails 3.2.x, so cherry-pick would be
    appreciated!
This page is out of date. Refresh to see the latest.
View
2  activerecord/lib/active_record/associations/collection_association.rb
@@ -414,7 +414,7 @@ def merge_target_lists(persisted, memory)
persisted.map! do |record|
if mem_record = memory.delete(record)
- (record.attribute_names - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
end
View
8 activerecord/test/cases/associations/join_model_test.rb
@@ -231,6 +231,14 @@ def test_has_many_with_piggyback
assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s
end
+ def test_create_through_has_many_with_piggyback
+ category = categories(:sti_test)
+ ernie = category.authors_with_select.create(:name => 'Ernie')
+ assert_nothing_raised do
+ assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ end
+ end
+
def test_include_has_many_through
posts = Post.all.merge!(:order => 'posts.id').to_a
posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
Something went wrong with that request. Please try again.