Rails:update_column可以工作,但update_attributes不能

28

我有一个简单的模型:

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

在我的控制器中,我有一个简单的更新方法:

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

这并不会触发错误,但它也实际上没有更新回复。相反,我会收到“我正在尝试更新!”的消息,就好像一切都正常工作一样。但是当我重新加载页面并查看回复时,它仍然有相同的文本。它实际上并没有被更新。如果我将update_attributes替换为:

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

它运行良好。如果我使用:

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

它又一次不起作用了。你有什么想法吗?

在我的日志中,我有这个:

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)

你正在使用哪个版本的Rails? - Wasi
我正在使用版本3.2.11,这是3.2分支中的最新版本。 - nullnullnull
你可以使用 update_attributes! 来查看为什么没有保存。 - alestanis
除非我弄错了,它来自我的视图。有一行调用reply.post来测试用户权限。 - nullnullnull
@timothythehuman 为什么要在 update.js.erb 中使用 respond_to 格式块来包装代码呢? - Viren
显示剩余2条评论
3个回答

91

你正在使用的三种方法各自有不同的作用:

  • update_attributes 试图验证记录,调用回调函数保存
  • update_attribute 不验证记录,调用回调函数保存
  • update_column 不验证记录,不调用回调函数,不调用保存方法,但会在数据库中更新记录。

如果唯一“有效”的方法是 update_column,我的猜测是你有一个回调函数出现了错误。尝试检查你的 log/development.log 文件以了解发生了什么。

你也可以使用update_attributes!。这个变体会抛出一个错误,因此它可能会给你关于模型为什么没有保存的信息。

除非你确切知道自己在做什么,否则应该使用update_attributes并避免使用其他两种方法。如果稍后向模型添加验证和回调,使用update_attributeupdate_column可能会导致极难调试的不良行为。

你可以查看这个链接获取更多信息。


1
谢谢提供信息。奇怪的是,即使使用bang update_attributes!也没有抛出错误。我在日志文件中也没有发现任何错误,但可能我没有正确地阅读它。如果您不介意,请查看我的帖子中添加的日志。 - nullnullnull
这个答案在Rails 4中是错误或过时的:#update_column确实保存记录! - collimarco
@collimarco 嘿,不要太过激动。这个问题标记为“ruby-on-rails-3”,所以是关于Rails 3而不是Rails 4。 - alestanis
@alestanis 抱歉,这并不是要表现出攻击性 :) 问题在于有很多混淆的地方:如果你试图理解其中的区别,并阅读了这个答案,你会变得更加困惑。也许问题应该更新一下,说明它是关于Rails 3的,因为标签并不够明显。 - collimarco
我在Rails 3.2中遇到了同样的问题。唯一的区别是我正在尝试更新的列是一个:binary列,并且我正在使用Marshal.dump(val)来更新其值。update_column可以工作,但其他更新方式都不行。我最初认为这是在:binary列上使用Marshal的特定错误,但我猜它更为普遍? - jamesfzhang
显示剩余8条评论

0
一个直觉的猜测是你可能有一个大规模赋值的问题,应该在你的模型中添加属性,像这样:
attr_accessible: :your_attribute, :your_attribute2

那也是我的第一猜想。不幸的是,我已经将它(我正在更改的唯一属性)设置为attr_accessible。无论如何,谢谢! - nullnullnull

0
我遇到了同样的问题,但是是在Rails 4中。这个问题发生在你使用update_attribute时,params[]会出错。在Rails 4中,强制参数(strong parameters)才能解决这个问题。
@reply.update_attributes(params[reply_params])

应该是

@reply.update_attributes(reply_params)

我对Rails 3不是很熟悉,但这应该是问题所在:

@reply.update_attributes(params[:reply])

应该是

@reply.update_attributes(:reply)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接