19

The original, happily working version of my project looked like this:

1) User fills out form (new action) and hits submit (create action)

2) User is redirected to their edit page (edit action uses an edit_id created by model, not Rails auto-gen id), which shows the info user had already submitted

3) User can choose to change info (update action) and re-submit

In this version, even if the user changes nothing in the edit page and submits, the page will still flash a success alert.

From a database perspective, I don’t really care, because since the form is prefilled with the user’s info, the update_attributes method is just overriding old info with the same info.

From a user perspective though, it's annoying, so I want to ensure that the info is only updated and the success alert flashed only if the user actually changes something.

I thought this would be really easy, changing the old code from this:

def update
  @request = Request.find_by_edit_id(params[:edit_id])
  if @request.update_attributes(request_params) 
    flash[:success] = true
    redirect_to edit_request_path(@request.edit_id)
  else
    render 'edit'
  end
end

And adding one additional component to the "if" like this:

def update
  @request = Request.find_by_edit_id(params[:edit_id])
  if @request.update_attributes(request_params) && @request.changed?
    flash[:success] = true
    redirect_to edit_request_path(@request.edit_id)
  else
    render 'edit'
  end
end

But this doesn’t work. Now what happens is that, on the edit page, if I don’t change any info and hit submit, nothing happens (which is great), but if I DO change info and hit submit, still nothing happens (which is bad). What am I doing wrong?

Note: I initially thought it was an order of operations error, so I made it a nested if, with first if @request.update_attributes, and second if @request.changed, but this didn't work either...

james
  • 3,989
  • 8
  • 47
  • 102

1 Answers1

32

The update_attributes method includes the 'save' call as part of its method and is returning true if the save is successful. I think you're looking for something like this using assign_attributes:

def update
  @request = Request.find_by_edit_id(params[:edit_id])
  @request.assign_attributes(request_params)

  if @request.changed?
    if @request.save
      flash[:success] = true
      redirect_to edit_request_path(@request.edit_id)
    else
      render 'edit'
    end
  else
    # Action if no change has been made
  end
end
Helios de Guerra
  • 3,445
  • 18
  • 23
  • Beautiful thanks. Another way that a friend shared was to write: "@request.attributes = request_params" before the if, and then use the following combined if statement: "if @request.changed? && @request.save" – james Jan 23 '14 at 03:28
  • 2
    `@request.attributes` is an alias for `assign_attributes`, aka the same – Ode Sep 10 '15 at 06:10
  • how to do it for nested attributes? for example, a user has_many: posts @user.assign_attributes(user_params) like @posts = user.posts @ @posts.assign_attributes(???????) – Giridharan Feb 19 '20 at 12:12
  • How does this solution handle partial records and missing fields? so if I have user with 4 fields email, name, age, date and i gt an update post with email and age on t and the other 2 are blank will it update the records to the blank value ? – Steven Moffat Mar 09 '21 at 19:13