This project is archived and is in readonly mode.

#474 ✓resolved
David Dollar

Allow Hash to be passed into AssociationProxy setters and AssociationCollections

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

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/...

Comments and changes to this ticket

  • David Dollar

    David Dollar June 30th, 2008 @ 04:39 PM

    • Tag set to activerecord, patch

    Added patch file

  • David Dollar

    David Dollar July 2nd, 2008 @ 09:35 PM

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

  • David Dollar
  • Pascal Ehlert

    Pascal Ehlert July 4th, 2008 @ 08:29 AM

    • Tag changed from activerecord, patch to activerecord, patch, tested

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

  • Pratik

    Pratik July 8th, 2008 @ 02:08 PM

    • Assigned user set to “Pratik”
  • Pratik

    Pratik July 14th, 2008 @ 12:42 AM

    • no changes were found...
  • David Dollar

    David Dollar July 14th, 2008 @ 02:22 AM

    Reworked as has_many :foo, :accessible => true

  • David Dollar
  • Repository

    Repository July 14th, 2008 @ 02:55 AM

    (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...

  • Pratik

    Pratik July 14th, 2008 @ 03:00 AM

    • State changed from “new” to “resolved”
  • Pascal Ehlert

    Pascal Ehlert July 14th, 2008 @ 10:39 AM

    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.

  • David Dollar

    David Dollar July 14th, 2008 @ 01:47 PM

    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">
    
    
  • Pascal Ehlert

    Pascal Ehlert July 14th, 2008 @ 02:02 PM

    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.

  • David Dollar

    David Dollar July 14th, 2008 @ 02:10 PM

    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:

  • David Dollar
  • David Dollar

    David Dollar July 14th, 2008 @ 05:01 PM

    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">]
    
    
  • James

    James July 15th, 2008 @ 02:43 AM

    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

  • David Dollar

    David Dollar July 15th, 2008 @ 02:50 AM

    • no changes were found...
  • David Dollar

    David Dollar July 15th, 2008 @ 05:42 AM

    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.

  • Repository

    Repository September 10th, 2008 @ 06:51 PM

    (from [9994f0d90248db7d7eae36f0b597a15e8a427612]) Revert "Add :accessible option to Associations for allowing mass assignments using hash. [#474 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...

  • Brian Mitchell

    Brian Mitchell September 10th, 2008 @ 09:16 PM

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

  • Sean Kirby
  • Andrew Vit

    Andrew Vit November 26th, 2008 @ 09:11 AM

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

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Referenced by

Pages