Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Allow Hash to be passed into AssociationProxy setters and AssociationCollections

#474

Currently when creating a nested form

form_for @books do |f|

f.text_field :isbn

f.fields_for :author do |af|

af.text_field :name

end

end

Book.new(params[:book]) fails because Book#author is passed a HashWithIndifferentAccess (params[:book][:author]) rather than an actual Author

This patch allows Hash to be used in place of an actual ActiveRecord model, and the model itself will be hydrated from the Hash as needed.

Patch is at http://github.com/ddollar/rails/...

Reported by David Dollar · June 23rd, 2008 @ 07:34 PM

State: resolved
Milestone: none
Assigned to: Pratik Pratik
Importance: none

Activity

  1. David Dollar
    David Dollar
    • Tag set to activerecord, patch

    Added patch file

    June 30th, 2008 @ 04:39 PM

  2. David Dollar
    David Dollar

    Refactored the patch to add ActiveRecord::Base#attr_creatable to permit this functionality

    July 2nd, 2008 @ 09:35 PM

  3. David Dollar
  4. Pascal Ehlert
    Pascal Ehlert
    • Tag changed from activerecord, patch to activerecord, patch, tested

    Checked it out, tests are passing. Looks good, would like to see this feature soon.

    July 4th, 2008 @ 08:29 AM

  5. Pratik
    Pratik
    • Assigned user set to Pratik

    July 8th, 2008 @ 02:08 PM

  6. Pratik
  7. David Dollar
    David Dollar

    Reworked as has_many :foo, :accessible => true

    July 14th, 2008 @ 02:22 AM

  8. David Dollar
  9. Repository
    Repository

    (from [e0750d6a5c7f621e4ca12205137c0b135cab444a]) Add :accessible option to Associations for allowing mass assignments using hash. [#474 state:resolved]

    Allows nested Hashes (i.e. from nested forms) to hydrate the appropriate

    ActiveRecord models.

    class Post < ActiveRecord::Base

    belongs_to :author, :accessible => true

    has_many :comments, :accessible => true

    end

    post = Post.create({

    :title => 'Accessible Attributes',

    :author => { :name => 'David Dollar' },

    :comments => [

    { :body => 'First Post!' },

    { :body => 'Nested Hashes are great!' }

    ]

    })

    post.comments << { :body => 'Another Comment' }

    Signed-off-by: Pratik Naik

    http://github.com/rails/rails/co...

    July 14th, 2008 @ 02:55 AM

  10. Pratik
    Pratik
    • State changed from new to resolved

    July 14th, 2008 @ 03:00 AM

  11. Pascal Ehlert
    Pascal Ehlert

    Okay, I'm a bit in a hurry now and just want to make a quick note before I forgot. I will go into more detail about this later.

    If we now also allow attributes for has_many associations, wouldn't it make sense to do it in the way that AttributeFu does it, in order to make it possible to create/update both, existing and new records?

    I actually see few cases were only creation is useful.

    July 14th, 2008 @ 10:39 AM

  12. David Dollar
    David Dollar

    I'm not sure what you mean. Updating currently works with this change.

    
    class Author < ActiveRecord::Base
      has_many :posts, :accessible => true
    end
    
    class Post < ActiveRecord::Base
      belongs_to :author, :accessible => true
    end
    
    
    > Post.create({:title => 'Foo', :author => { :name => 'David' }})
    => #<Post id: 1, author_id: 1, title: "Foo", created_at: "2008-07-14 12:42:39", updated_at: "2008-07-14 12:42:39">
    
    > p = Post.find(1)
    => #<Post id: 1, author_id: 1, title: "Foo", created_at: "2008-07-14 12:42:39", updated_at: "2008-07-14 12:42:39">
    
    > p.author = { :name => 'Joe' }
    => {:name=>"Joe"}> p.save
    => true
    
    > p
    => #<Post id: 1, author_id: 2, title: "Foo", created_at: "2008-07-14 12:42:39", updated_at: "2008-07-14 12:43:11">
    
    > p.author
    => #<Author id: 2, name: "Joe", created_at: "2008-07-14 12:43:11", updated_at: "2008-07-14 12:43:11">
    
    > a = Author.find(2)
    => #<Author id: 2, name: "Joe", created_at: "2008-07-14 12:43:11", updated_at: "2008-07-14 12:43:11">
    
    > a.posts
    => [#<Post id: 1, author_id: 2, title: "Foo", created_at: "2008-07-14 12:42:39", updated_at: "2008-07-14 12:43:11">]
    
    > a.posts << { :title => 'Another Post' }
    => [#<Post id: 1, author_id: 2, title: "Foo", created_at: "2008-07-14 12:42:39", updated_at: "2008-07-14 12:43:11">, #<Post id: 2, author_id: 2, title: "Another Post", created_at: "2008-07-14 12:45:49", updated_at: "2008-07-14 12:45:49">]
    
    > a.posts.last
    => #<Post id: 2, author_id: 2, title: "Another Post", created_at: "2008-07-14 12:45:49", updated_at: "2008-07-14 12:45:49">
    
    

    July 14th, 2008 @ 01:47 PM

  13. Pascal Ehlert
    Pascal Ehlert

    Sure, it works for has_one and belongs_to, but not so well for has_many.

    It will only create a new record and delete the old one which I don't think is a good behavior.

    July 14th, 2008 @ 02:02 PM

  14. David Dollar
    David Dollar

    I think I see what you mean. Let me see what I can come up with.

    --

    David Dollar

    ddollar@gmail.com

    On Jul 14, 2008, at 9:02 AM, Lighthouse

    wrote:

    July 14th, 2008 @ 02:10 PM

  15. David Dollar
  16. David Dollar
    David Dollar

    Adds the ability to update existing entries on :accessible => true associations

    
    # create from a basic hash
    > p = Post.create(:title => 'Test Post', :author => { :name => 'David' })
    => #<Post id: 8, author_id: 8, title: "Test Post", created_at: "2008-07-14 15:22:53", updated_at: "2008-07-14 15:22:53">
    
    # update a singular reference
    > p.author = { :name => 'Joe' }
    => {:name=>"Joe"}
    
    # it 'updates' the row in sql, notice the id is still the same
    > p.author
    => #<Author id: 8, name: "Joe", created_at: "2008-07-14 15:22:53", updated_at: "2008-07-14 15:23:03">
    
    
    # create an author with posts from a hash
    >  a = Author.create(:name => 'David', :posts => [ { :title => 'Post 1' }, { :title => 'Post 2' } ])
    => #<Author id: 14, name: "David", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:18">
    
    # show the posts
    > a.posts
    => [#<Post id: 17, author_id: 14, title: "Post 1", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:18">, #<Post id: 18, author_id: 14, title: "Post 2", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:18">]
    
    # use << to update existing entries (as well as add new ones, demonstrated later)
    > a.posts << { :id => 17, :title => 'Post 1 Updated' }
    => [#<Post id: 17, author_id: 14, title: "Post 1 Updated", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:53">, #<Post id: 18, author_id: 14, title: "Post 2", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:18">]
    
    # show posts to verify the update
    > a.posts
    => [#<Post id: 17, author_id: 14, title: "Post 1 Updated", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:53">, #<Post id: 18, author_id: 14, title: "Post 2", created_at: "2008-07-14 15:38:18", updated_at: "2008-07-14 15:38:18">]
    
    # can't update posts that don't belong to the author
    > a.posts << { :id => 1, :title => 'Not Allowed' }
    ActiveRecord::RecordNotFound: Couldn't find Post with ID=1 AND ("posts".author_id = 14)
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/base.rb:1393:in `find_one'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/base.rb:1376:in `find_from_ids'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/base.rb:537:in `find'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:47:in `find'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:103:in `<<'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:99:in `each'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:99:in `<<'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in `transaction'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/transactions.rb:79:in `transaction'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/transactions.rb:98:in `transaction'
            from /Users/ddollar/Code/EdgeRailsApp/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:98:in `<<'
            from (irb):12
            
    # use = to outright replace all posts
    > a.posts = [ { :title => 'Replace Posts' } ]
    => [#<Post id: 19, author_id: 14, title: "Replace Posts", created_at: "2008-07-14 15:40:30", updated_at: "2008-07-14 15:40:30">]
    
    # can even 'replace' using existing posts, the post attributes will be updated
    > a.posts = [ { :id => 19, :title => 'Can Replace This Way Too' } ]
    => [#<Post id: 19, author_id: 14, title: "Can Replace This Way Too", created_at: "2008-07-14 15:40:30", updated_at: "2008-07-14 15:40:49">]
    
    # use << also for adding brand new items
    > a.posts << { :title => 'New Post' }
    => [#<Post id: 19, author_id: 14, title: "Replace Posts", created_at: "2008-07-14 15:40:30", updated_at: "2008-07-14 15:40:30">, #<Post id: 20, author_id: 14, title: "New Post", created_at: "2008-07-14 15:43:18", updated_at: "2008-07-14 15:43:18">]
    
    

    July 14th, 2008 @ 05:01 PM

  17. James
    James

    This approach seems fairly limited.

    We couldn't have a form, for example, that would be able to update, and create new child models at once.

    My plugin, attribute_fu (mentioned earlier), does the same thing, but supports all the cases where there are both new and saved objects in the hash of hashes. It is a fully transparent solution for creating multi-model forms, including form helpers.

    Is there any chance of extending this syntax to support those situations, or sucking attribute_fu down in to core?

    If you're unfamiliar with the plugin, checkout this screencast

    July 15th, 2008 @ 02:43 AM

  18. David Dollar
    David Dollar
    • No changes were found…

    July 15th, 2008 @ 02:50 AM

  19. David Dollar
    David Dollar

    The second patch mentioned here adds all of the features you are looking for. To mix both creating and updating simply specify some elements of the hash with :id keys and others without.

    July 15th, 2008 @ 05:42 AM

  20. Repository
    Repository

    (from [9994f0d90248db7d7eae36f0b597a15e8a427612]) Revert "Add :accessible option to Associations for allowing mass assignments using hash. [#474 Allow Hash to be passed into AssociationProxy setters and AssociationCollections state:resolved]"

    This reverts commit e0750d6a5c7f621e4ca12205137c0b135cab444a.

    Conflicts:

    activerecord/CHANGELOG
    activerecord/lib/active_record/associations.rb
    activerecord/lib/active_record/associations/association_collection.rb
    
    

    http://github.com/rails/rails/co...

    September 10th, 2008 @ 06:51 PM

  21. Brian Mitchell
    Brian Mitchell

    Any word on what might replace this functionality or is it gone for the time being?

    September 10th, 2008 @ 09:16 PM

  22. Sean Kirby
  23. Andrew Vit
    Andrew Vit

    Why is this ticket marked "resolved" if the feature was pulled?

    November 26th, 2008 @ 09:11 AM