2013-05-22 5 views
11

In Rails Merging 4 ist es möglich, zusätzliche Parameter mit nutzergenerierten diejenigen zu fusionieren, wie so:Rails: eine verschachtelte Attribut mit strong_params

params.require(:post).permit([:title, :body]).merge(user: current_user) 

Es ist auch möglich, verschachtelte Attribute sind wie folgt:

params.require(:post).permit([:title, :body, sections_attributes: [:title, :section_type]]) 

Was nun, wenn ich zusätzliche Parameter in ein verschachteltes Modell zusammenführen möchte. Ich habe versucht, dieses:

params.require(:post).permit([:title, :body, sections_attributes: [:title, :section_type]]).merge(user: current_user, sections_attributes: [user: current_user]) 

Aber wenn ich die params mit Debugger danach überprüfen, finde ich, dass user die andere section_attributes anstatt Verschmelzung mit ihnen überschrieben hat. Gibt es einen besseren Weg, dieses Problem anzugehen?

Full backtrace 
-------------- 

- activemodel (4.0.0.rc1) lib/active_model/attribute_methods.rb:436:in `method_missing' 
- activerecord (4.0.0.rc1) lib/active_record/attribute_methods.rb:131:in `method_missing' 
- activerecord (4.0.0.rc1) lib/active_record/nested_attributes.rb:432:in `block in assign_nested_attributes_for_collection_association' 
- activerecord (4.0.0.rc1) lib/active_record/nested_attributes.rb:431:in `assign_nested_attributes_for_collection_association' 
- activerecord (4.0.0.rc1) lib/active_record/nested_attributes.rb:322:in `comments_attributes=' 
- activerecord (4.0.0.rc1) lib/active_record/attribute_assignment.rb:42:in `_assign_attribute' 
- activerecord (4.0.0.rc1) lib/active_record/attribute_assignment.rb:53:in `block in assign_nested_parameter_attributes' 
- activerecord (4.0.0.rc1) lib/active_record/attribute_assignment.rb:53:in `assign_nested_parameter_attributes' 
- activerecord (4.0.0.rc1) lib/active_record/attribute_assignment.rb:33:in `assign_attributes' 
- activerecord (4.0.0.rc1) lib/active_record/core.rb:192:in `initialize' 
- activerecord (4.0.0.rc1) lib/active_record/inheritance.rb:27:in `new' 
- activerecord (4.0.0.rc1) lib/active_record/reflection.rb:189:in `build_association' 
- activerecord (4.0.0.rc1) lib/active_record/associations/association.rb:235:in `build_record' 
- activerecord (4.0.0.rc1) lib/active_record/associations/has_many_through_association.rb:102:in `build_record' 
- activerecord (4.0.0.rc1) lib/active_record/associations/collection_association.rb:114:in `build' 
- activerecord (4.0.0.rc1) lib/active_record/associations/collection_proxy.rb:229:in `build' 
- app/controllers/forum/topics_controller.rb:16:in `create' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/implicit_render.rb:4:in `send_action' 
- actionpack (4.0.0.rc1) lib/abstract_controller/base.rb:189:in `process_action' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/rendering.rb:10:in `process_action' 
- actionpack (4.0.0.rc1) lib/abstract_controller/callbacks.rb:18:in `block in process_action' 
- activesupport (4.0.0.rc1) lib/active_support/callbacks.rb:422:in `_run__642753351245287313__process_action__callbacks' 
- activesupport (4.0.0.rc1) lib/active_support/callbacks.rb:80:in `run_callbacks' 
- actionpack (4.0.0.rc1) lib/abstract_controller/callbacks.rb:17:in `process_action' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/rescue.rb:29:in `process_action' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action' 
- activesupport (4.0.0.rc1) lib/active_support/notifications.rb:159:in `block in instrument' 
- activesupport (4.0.0.rc1) lib/active_support/notifications/instrumenter.rb:20:in `instrument' 
- activesupport (4.0.0.rc1) lib/active_support/notifications.rb:159:in `instrument' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/instrumentation.rb:30:in `process_action' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/params_wrapper.rb:245:in `process_action' 
- activerecord (4.0.0.rc1) lib/active_record/railties/controller_runtime.rb:18:in `process_action' 
- actionpack (4.0.0.rc1) lib/abstract_controller/base.rb:136:in `process' 
- actionpack (4.0.0.rc1) lib/abstract_controller/rendering.rb:44:in `process' 
- actionpack (4.0.0.rc1) lib/action_controller/metal.rb:195:in `dispatch' 
- actionpack (4.0.0.rc1) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch' 
- actionpack (4.0.0.rc1) lib/action_controller/metal.rb:231:in `block in action' 
- actionpack (4.0.0.rc1) lib/action_dispatch/routing/route_set.rb:80:in `dispatch' 
- actionpack (4.0.0.rc1) lib/action_dispatch/routing/route_set.rb:48:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/journey/router.rb:71:in `block in call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/journey/router.rb:59:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/routing/route_set.rb:654:in `call' 
- request_store (1.0.5) lib/request_store/middleware.rb:9:in `call' 
- warden (1.2.1) lib/warden/manager.rb:35:in `block in call' 
- warden (1.2.1) lib/warden/manager.rb:34:in `call' 
- rack (1.5.2) lib/rack/etag.rb:23:in `call' 
- rack (1.5.2) lib/rack/conditionalget.rb:35:in `call' 
- rack (1.5.2) lib/rack/head.rb:11:in `call' 
- () home/timothythehuman/.rvm/gems/[email protected]/bundler/gems/remotipart-2d6e0949acc2/lib/remotipart/middleware.rb:30:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/params_parser.rb:27:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/flash.rb:241:in `call' 
- rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context' 
- rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/cookies.rb:486:in `call' 
- activerecord (4.0.0.rc1) lib/active_record/query_cache.rb:36:in `call' 
- activerecord (4.0.0.rc1) lib/active_record/connection_adapters/abstract/connection_pool.rb:626:in `call' 
- activerecord (4.0.0.rc1) lib/active_record/migration.rb:366:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call' 
- activesupport (4.0.0.rc1) lib/active_support/callbacks.rb:392:in `_run__4051735323972233883__call__callbacks' 
- activesupport (4.0.0.rc1) lib/active_support/callbacks.rb:80:in `run_callbacks' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/callbacks.rb:27:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/reloader.rb:64:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/remote_ip.rb:76:in `call' 
- better_errors (0.9.0) lib/better_errors/middleware.rb:84:in `protected_app_call' 
- better_errors (0.9.0) lib/better_errors/middleware.rb:79:in `better_errors_call' 
- better_errors (0.9.0) lib/better_errors/middleware.rb:56:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call' 
- railties (4.0.0.rc1) lib/rails/rack/logger.rb:38:in `call_app' 
- railties (4.0.0.rc1) lib/rails/rack/logger.rb:21:in `block in call' 
- activesupport (4.0.0.rc1) lib/active_support/tagged_logging.rb:67:in `block in tagged' 
- activesupport (4.0.0.rc1) lib/active_support/tagged_logging.rb:25:in `tagged' 
- activesupport (4.0.0.rc1) lib/active_support/tagged_logging.rb:67:in `tagged' 
- railties (4.0.0.rc1) lib/rails/rack/logger.rb:21:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/request_id.rb:21:in `call' 
- rack (1.5.2) lib/rack/methodoverride.rb:21:in `call' 
- rack (1.5.2) lib/rack/runtime.rb:17:in `call' 
- activesupport (4.0.0.rc1) lib/active_support/cache/strategy/local_cache.rb:83:in `call' 
- rack (1.5.2) lib/rack/lock.rb:17:in `call' 
- actionpack (4.0.0.rc1) lib/action_dispatch/middleware/static.rb:64:in `call' 
- railties (4.0.0.rc1) lib/rails/engine.rb:511:in `call' 
- railties (4.0.0.rc1) lib/rails/application.rb:96:in `call' 
- rack (1.5.2) lib/rack/content_length.rb:14:in `call' 
- thin (1.5.1) lib/thin/connection.rb:81:in `block in pre_process' 
- thin (1.5.1) lib/thin/connection.rb:79:in `pre_process' 
- thin (1.5.1) lib/thin/connection.rb:54:in `process' 
- thin (1.5.1) lib/thin/connection.rb:39:in `receive_data' 
- eventmachine (1.0.3) lib/eventmachine.rb:187:in `run' 
- thin (1.5.1) lib/thin/backends/base.rb:63:in `start' 
- thin (1.5.1) lib/thin/server.rb:159:in `start' 
- rack (1.5.2) lib/rack/handler/thin.rb:16:in `run' 
- rack (1.5.2) lib/rack/server.rb:264:in `start' 
- railties (4.0.0.rc1) lib/rails/commands/server.rb:84:in `start' 
- railties (4.0.0.rc1) lib/rails/commands.rb:80:in `block in <top (required)>' 
- railties (4.0.0.rc1) lib/rails/commands.rb:75:in `<top (required)>' 
- bin/rails:4:in `<main>' 

Die params:

{"name"=>"stuff", "description"=>"", "topical_id"=>"1", "topical_type"=>"User", "comments_attributes"=>{"0"=>{"body"=>"1111111111111111111"}, "user"=>#<User id: 1, active: true, bio: nil, birthday: nil, image: nil, location: nil, real_name: nil, twitter_name: nil, username: "tbaron", website: nil, whuffie: #<BigDecimal:6365488,'0.0',9(18)>, slug: nil, created_at: "2013-05-31 00:42:28", updated_at: "2013-05-31 00:42:35", email: "[email protected]", encrypted_password: "$2a$10$jSrDsC9Ai.yFU5sttCxIiuRBthDUYiy9wWyZnie70qbp...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2013-05-31 00:42:35", last_sign_in_at: "2013-05-31 00:42:35", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", confirmation_token: nil, confirmed_at: nil, confirmation_sent_at: nil, unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil>}, "user"=>#<User id: 1, active: true, bio: nil, birthday: nil, image: nil, location: nil, real_name: nil, twitter_name: nil, username: "tbaron", website: nil, whuffie: #<BigDecimal:6365488,'0.0',9(18)>, slug: nil, created_at: "2013-05-31 00:42:28", updated_at: "2013-05-31 00:42:35", email: "[email protected]", encrypted_password: "$2a$10$jSrDsC9Ai.yFU5sttCxIiuRBthDUYiy9wWyZnie70qbp...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2013-05-31 00:42:35", last_sign_in_at: "2013-05-31 00:42:35", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", confirmation_token: nil, confirmed_at: nil, confirmation_sent_at: nil, unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil>} 

Antwort

7

Die merge Methode, die Sie aufrufen sind nur die übliche Methode Ruby-Hash#merge. Für jeden Schlüssel im Argument merge überschreibt der Wert den Wert, der aktuell für diesen Schlüssel vorhanden ist, falls vorhanden. In diesem Fall überschreiben Sie den Wert sections_attributes.

Da sections_attributes ist ein "Pseudo-Array" der Form {"0" => first_hash, "1" => second_hash} und so weiter, müssen Sie eine Möglichkeit, Ihre user einmal pro Eintrag zu duplizieren. Sie können das tun, indem Sie die Fähigkeit der Zusammenführung verwenden, einen Block nehmen, der den zusammengeführten Wert ändert. Hier ist ein Ansatz:

filtered_params = params.require(:post) 
         .permit([:title, 
           :body, 
           sections_attributes: [:title, :section_type]]) 
additional_params = {user: current_user, sections_attributes: [user: current_user]} 
result = filtered_params.merge(additional_params) do |key, oldval, newval| 
    if newval.is_a? Array 
    # Arrays are expected to be one-element arrays containing a Hash that is 
    # supposed to be merged into each element of the currently-existing 
    # "pseudo-array" 
    oldval ||= {} 
    Hash[oldval.map {|k, v| [k, v.merge(newval.first)]}] 
    elsif newval.is_a? Hash 
    # Hashes are merged into existing hashes 
    oldval ||= {} 
    oldval.merge newval 
    else 
    # Other types are passed as-is (and replace any existing value) 
    newval 
    end 
end 

# This marks the newly added parameters as permitted. It's only necessary because we 
# made new Hashes when we modified the "pseudo-array" 
result.permit! 

Wenn Sie feststellen, dass dies für Sie arbeitet, können Sie dies in einem Verfahren einkapseln könnten, entweder durch das Innere des Blockes zu nehmen und es über ein separates Verfahren zu machen, oder durch die ganze Sache Installation als eine benannte Methode in der Klasse Parameters.

Beachten Sie auch, dass das obige nur eine Rekursionsschicht behandelt. Tiefer zu gehen ist ein bisschen schwierig. Wenn Sie es tun müssen, würde ich gerne eine Erklärung schreiben.

+0

Dies scheint ein Schritt in die richtige Richtung zu sein. Wenn ich jedoch 'deep_merge' auf die Parameter anwende, bekomme ich den Fehler 'undefinierte Methode' mit_indifferent_access' für # '. – nullnullnull

+0

Interessant. Können Sie ein Backtrace für den Fehler buchen? – charleyc

+0

Danke, dass du dir das angeschaut hast. Ich habe das Backtrace oben hinzugefügt. – nullnullnull

Verwandte Themen