28

I have a simple model:

class Reply < ActiveRecord::Base
  attr_accessible :body
  belongs_to :post
end

In my controller, I have a simple update method:

def update
  @reply = Reply.find(params[:id])
  if @reply.update_attributes!(params[:reply])
    render :js => "alert('I am trying to update!')"
  else
    render :js => "alert('<%= @reply.errors %>')"
  end
end

This doesn't throw an error, but neither does it actually update the reply. Instead, I get the "I am trying to update!" message, like everything worked. But when I reload the page and look at the reply, it has the same text. It hasn't actually been updated. If I replace update_attributes with:

@reply.update_column(:body, params[:reply][:body])

It works fine. If I use:

@reply.update_attribute(:body, params[:reply][:body])

It once again doesn't work. Any idea what's going?

In my log, I have this:

Started PUT "/posts/2/replies/20" for 127.0.0.1 at 2013-01-19 10:39:57 -0600
Processing by RepliesController#update as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Xot7E+ldXiBm0hVvw5XUP/U5guJU2g8e4QaLbDVGzDE=", "reply"=>{"body"=>"Updated text."}, "commit"=>"Submit Revision", "post_id"=>"2", "id"=>"20"
[1m[35mUser Load (1.0ms)[0m  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
[1m[36mReply Load (0.0ms)[0m  [1mSELECT `replies`.* FROM `replies` WHERE `replies`.`id` = 20 LIMIT 1[0m
[1m[35m (1.0ms)[0m  BEGIN
[1m[36mPost Load (0.0ms)[0m  [1mSELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1[0m
[1m[35m (0.0ms)[0m  COMMIT
Rendered replies/_reply_content.html.erb (502.0ms)
Rendered replies/update.js.erb (505.0ms)
Completed 200 OK in 849ms (Views: 484.0ms | ActiveRecord: 94.0ms)
nullnullnull
  • 8,039
  • 12
  • 55
  • 107

3 Answers3

90

The three methods you are using do different things:

  • update_attributes tries to validate the record, calls callbacks and saves;
  • update_attribute doesn't validate the record, calls callbacks and saves;
  • update_column doesn't validate the record, doesn't call callbacks, doesn't call save method, though it does update record in the database.

If the only method that "works" is update_column my guess is that you have a callback somewhere that is throwing an error. Try to check your log/development.log file to see what's going on.

You can also use update_attributes!. This variant will throw an error, so it may give you information on why your model isn't saving.

You should use update_attributes and avoid the two other methods unless you know exactly what you are doing. If you add validations and callbacks later to your model, using update_attribute and update_column can lead to nasty behaviour that is really difficult to debug.

You can check this link for more info on that.

kgorin
  • 3
  • 2
alestanis
  • 21,519
  • 4
  • 48
  • 67
  • 1
    Thanks for the info. Strangely, even with the bang update_attributes! isn't throwing an error. I can't spot any errors in my log file, either, though maybe I'm not reading it correctly. I've added the log to my post, if you wouldn't mind giving it a look. – nullnullnull Jan 19 '13 at 16:54
  • I've updated question with more information. Specifically, I've put a condition into the controller so that it will throw errors if there are any. Instead, it acts like it's saving properly. Something is very strange here, right? – nullnullnull Jan 19 '13 at 17:26
  • @timothythehuman Your condition is useless with the bang version. It will raise an error anyway if it can't save so you'll never get to the `@reply.errors` line. You should use the normal version if you want to print any errors. And yes, something *is* very strange... – alestanis Jan 19 '13 at 17:29
  • Removed the bang, but it's giving me the "I am trying to update" message. Any other direction you can offer? I'll keep tinkering with this regardless. As you mention, falling on back on update_column is not ideal. – nullnullnull Jan 19 '13 at 17:34
  • @timothythehuman When you say it works with `update_column` do you mean the object is rendered in the view or that it's saved in the database? Do you call `save` after `update_column`? – alestanis Jan 19 '13 at 17:46
  • I mean it's saved to the database. With update_attributes it renders in the view but doesn't actually save. I've tried calling save as well, but with no effect. – nullnullnull Jan 19 '13 at 17:53
  • @timothythehuman I'm sorry, your code looks correct, I don't have any more ideas about what could be going wrong... :( – alestanis Jan 19 '13 at 17:55
  • Thanks for trying, though. I'll keep you posted when I figure this out. – nullnullnull Jan 19 '13 at 20:22
  • This answer is WRONG or outdated in Rails 4: #update_column does save the record! – collimarco Jul 26 '13 at 10:16
  • @collimarco Hey, don't be aggressive. The question is tagged 'ruby-on-rails-3' so yes, this is Rails 3 and not Rails 4. – alestanis Jul 27 '13 at 16:01
  • @alestanis Sorry, it wasn't meant to be aggressive :) The problem is that there's a lot of confusion out there: if you are trying to understand the difference and you read this answer you get even more confused. Maybe the question should be updated to say that it refers to Rails 3, because the tag is not enough visible. – collimarco Jul 27 '13 at 17:22
  • I am running into the same issue in Rails 3.2. The only difference is the column I am trying to update is a `:binary` column and I'm updating its value with `Marshal.dump(val)`. `update_column` works, but none of the other ways to update work. I initially thought it's an error specific to using `Marshal` on a `:binary` column, but I guess it's more widespread? – jamesfzhang Feb 05 '14 at 23:48
  • In your explanation for `update_column` what's the diff between "calling save method" and "updating the database"? @alestanis – Martin Verdejo Sep 13 '18 at 02:58
0

A gut guess would be to say that you have a mass assignment problem and should add your attributes in your model like this

attr_accessible: :your_attribute, :your_attribute2
Nakilon
  • 34,866
  • 14
  • 107
  • 142
Pasta
  • 1,750
  • 2
  • 14
  • 25
  • That was my first guess, too. Unfortunately, I have made it (the only attribute I'm changing) attr_accessible. Thanks, anyway! – nullnullnull Jan 24 '13 at 19:50
0

I had this same issue, but with Rails 4. The issue happens when you have params[] in update_attribute. In Rails 4 with strong parameters

@reply.update_attributes(params[reply_params])

should be

@reply.update_attributes(reply_params)

I'm not to familiar with Rails 3 but this should be the issue:

@reply.update_attributes(params[:reply])

should be

@reply.update_attributes(:reply)
ChrisBarthol
  • 4,871
  • 2
  • 21
  • 24