I need to present a form that is not backed by an AR model. (I know this question has been asked dozens of times in various way, but despite lots of reading an experimentation, I still can't get it right.)
The simplest way to describe what I need is by reverse engineering: Assume I want a form with a "username" field and a "accesscode" field. Then I want the [submit] button to call:
ServicesController#update
with the params hash set to (at least):
params={"service"=>{"credentials"=>{"username"=>"fred", "accesscode"=>"1234"}}, "commit"=>"update credentials", "action"=>"update", "controller"=>"services", "id"=>"54"}
or perhaps just
params={"credentials"=>{"username"=>"fred", "accesscode"=>"1234"}, "commit"=>"update credentials", "action"=>"update", "controller"=>"services", "id"=>"54"}
where '54' is the id of the Services object I want to update. (My update() method will pull the credentials out of the params hash and do the right thing with them.) (I haven't shown the routes, but I'm not sure that's relevant here.)
But I haven't yet figured out how to get form_tag or form_for to hew to my bidding. Suggestions?
Update As per aperture's suggestions below, a form_tag seems to be the right thing. I am getting a routing error 'No route matches "/services/54"'. Before I go modifying my current routes, I currently have:
resources :premises, :shallow => t开发者_开发知识库rue do
resources :services
end
which gives me (in part):
edit_service GET /services/:id/edit(.:format) {:action=>"edit", :controller=>"services"}
service GET /services/:id(.:format) {:action=>"show", :controller=>"services"}
PUT /services/:id(.:format) {:action=>"update", :controller=>"services"}
DELETE /services/:id(.:format) {:action=>"destroy", :controller=>"services"}
routes.rb
match '/services/update/:id', :to => 'services#update', :as => 'update_service'
Then your form:
- form_tag @service, :url => update_service_path do = text_field :credentials, :userid, 'value' => 'fred' = text_field :credentials, :accessid, 'value' => '1234'
then in your controller:
def update @service = Service.find_by_id(params[:id]) @service.update_attribute('userid', params[:credentials][:userid]) @service.update_attribute('accessid', params[:credentials][:accessid]) end
I don't know what the Services object is if it's not a model. So I don't know what method would be necessary to find the Service by id, and the update_attribute call probably won't work if it's not a model, but it's hard to say without more information
This is entirely untested. But hopefully is close to working...
I give the checkmark to @aperture, but here's the final version based on aperture's guidance after all the tweaks have been applied:
<%= form_tag @service, :url => service_path(@service), :method => :put do %>
<%= text_field :service, 'credentials[userid]' %>
<%= text_field :service, 'credentials[accessid]' %>
<%= submit_tag 'update credentials' %>
<% end %>
Clicking the [submit] button ends up calling Service#update() with the params hash set to:
params={... "_method"=>"put", "service"=>{"credentials"=>{"userid"=>"xxx", "accessid"=>"xxx"}}, "commit"=>"update credentials", "action"=>"update", "controller"=>"metered_services", "id"=>"54"}
Note that I've modified the text_field args from @aperture's version -- this approach wraps the credentials in their own credentials hash, so the Service#update method can be the canonical:
def update
@service = Service.find(params[:id])
@premise = @service.premise
if (@service.update_attributes(params[:service]))
redirect_to edit_premise_path(@premise), :notice => 'info was successfully updated'
else
redirect_to edit_premise_path(@premise), :error => 'could not update information'
end
end
... and any special handling of credentials resides (as it should) in the Service model.
精彩评论