On Jul 18, 2008, at 10:52 AM, Ryan Bates wrote:
> > Good point David, this can be very dangerous if always enabled due to > form injection. But when intentionally enabled, this has the potential > to greatly simplify multi-model forms - which I love! > > Out of curiosity, have you tried making a multi-model form with this? > I can if you want. There are a lot of complexities involved, > specifically with regards to when the associated models are saved and > what happens when validation fails. Yep, exactly. I talked with David Dollar on IRC about this. I've done this in a way that is similar to your recipe in ARR, but I create/ update the associated models in a before_validation callback, rather than in the setter. That keeps everything in one transaction and makes it a little easier to handle validation issues. I did like your approach of splitting the setters into two pieces for create and update. The one thing that keeps bugging me though is doing deletes - I've still yet to figure out a really nice way to handle that. --josh > > > Ryan > > > On Jul 17, 4:41 pm, David Dollar <[EMAIL PROTECTED]> wrote: >>> On Jul 16, 7:57 pm, Pratik <[EMAIL PROTECTED]> wrote: >>>> Has anyone any strong opinions against this ? I'm very interested >>>> in >>>> hearing more thoughts on this and/or if you have any suggestions to >>>> improvise David's approaches. >> >>>> Thanks, >>>> Pratik >> >>> Is the :accessible specifier necessary? In other words, are we >>> afraid >>> something might break if ActiveRecord::Base#new and #create natively >>> understood nested hashes as well as the flat hashes that it can >>> accept >>> today? >> >>> Jeff >> >> The main concern that I have seen presented is that form injection >> could cause data to be created/updated around your database >> that you did not intend should the attacker carefully craft a form >> for >> submission. >> >> David >> >>>> On Jul 14, 6:38 pm, David Dollar <[EMAIL PROTECTED]> wrote: >> >>>>> Recently the following patch >> >>>>> http://github.com/rails/rails/commit/e0750d6a5c7f621e4ca12205137c0b13 >>>>> ... >> >>>>> was committed to rails trunk. This patch allows for associations >>>>> to be >>>>> flagged as :accessible => true and then hydrated from nested >>>>> hashes >>>>> (i.e. nested forms) >> >>>>> 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' } >> >>>>> I have done some work on another patch to allow this same >>>>> mechanism to >>>>> be used for updating existing rows using nested hashes. This >>>>> completes >>>>> the work as far as dynamically generating forms and being able to >>>>> simply and intuitively push them back into the database. >> >>>>> http://github.com/ddollar/rails/commit/14a16844bbb3ba9edb14269ce2d0b6 >>>>> ... >> >>>>> This allows for the following: >> >>>>> # 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 >> >>>>> The patch can be found at >> >>>>> http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/61 >>>>> ... >> >>>>> I was asked to submit this patch to the mailing list and solicit >>>>> any >>>>> comments. Does anyone see any obvious holes or ways this >>>>> functionality >>>>> should change? >> >>>>> Thanks, >>>>> David Dollar >> >> smime.p7s >> 2KDownload > > -- Josh Susser http://blog.hasmanythrough.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---