As far as I'm aware this is the standard rails pattern for editing and updating a resource.
I have a GET route for the edit action on /users/:id/edit
and both a PUT and PATCH route for the update action on /users/:id
.
Here are my controller actions:
def edit
@user = User.find params[:id]
end
def update
@user = User.find params[:id]
if @user.update_attributes(user_params)
redirect_to @user, success: "Changes saved"
else
flash.now.alert = "Unable to change account details"
render :edit
end
end
And I'm using the default form_for in my form partial:
<%= form_for @user do |f| %>
which is creating the following form in the html:
<form accept-charset="UTF-8" action="/users/1" id="edit_user_1" method="post">
...
<input name="_method" type="hidden" value="patch">
When the call to update_attributes
succeeds the user is being redirected correctly, however when the call fails (due to ActiveRecord validations) the render action displays the edit view correctly, but the path in the user's browser changes from /users/1/edit
to /users/1
.
From what I've read it appears this is the expected behaviour of rails, but that seems confusing to me as I thought the core idea behind REST was that a URL referred to a canonical view of a resource?
If I redirect_to
to the edit action instead of render
then the url is as it should be, however I lose the error messages on the form.
Is there a more sensible way to persist the URL and the error messages than dumping the errors into the session before using redirect_to? I'd like to avoid that if I can.
Update:
The reason I want the URL to persist (as well as the errors) is because I'm using current_page? to set active states in the navigation. Therefore the 'Edit' action in the menu loses it's highlighting if the form is submitted with errors.
The reason you were in /users/1
is the view is rendered by #update
, not #edit
.
By default resource the path of #update
is same as #show
, with different request method GET
and PUT
. So, when #update
renders HTML response, it's same as #show
.
I think the result is acceptable, no matter what path your are in the result is expected. As you already found, if using redirect
, the @user
instance will be changed to a branch new one thus losing errors and previous fillings.
There is workaround on the path such as using session to pass instance variable, but I don't think it worth the effort. The current result is good enough.
Manipulating active states is easy. I don't like current_page?
which needs too much code. Use controller_path
and action_name
instead.
if controller_path == 'users' && (action_name == 'edit' || 'update')
# add active class
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With