例如,如果我在prop1
时运行test.update_attributes prop1: 'test', prop2: 'test2'
,并且prop2
具有阻止这些值的验证,则test.prop1
仍将'test'
,test.prop2
仍将'test2'
。为什么会发生这种情况,我该如何解决?
根据 Rails 文档 for update_attributes
,它是 update
的别名。其来源如下:
# File activerecord/lib/active_record/persistence.rb, line 247
def update(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save
end
end
因此,它被包装在数据库事务中,这就是回滚发生的原因。但是,让我们看看 assign_attributes
.根据其来源:
# File activerecord/lib/active_record/attribute_assignment.rb, line 23
def assign_attributes(new_attributes)
...
_assign_attribute(k, v)
...
end
这被定义为:
# File activerecord/lib/active_record/attribute_assignment.rb, line 53
def _assign_attribute(k, v)
public_send("#{k}=", v)
rescue NoMethodError
if respond_to?("#{k}=")
raise
else
raise UnknownAttributeError.new(self, k)
end
end
所以,当你打电话给test.update_attributes prop1: 'test', prop2: 'test'
时,它基本上归结为:
test.prop1 = 'test'
test.prop2 = 'test'
test.save
如果 save
未通过验证,我们的内存中副本test
仍具有修改后的prop1
和prop2
值。因此,我们需要使用 test.reload
并且问题得到解决(即我们的数据库和内存版本都保持不变)。
tl;dr 在失败的update_attributes
调用后使用test.reload
。
尝试将其包装在 if 语句中:
if test.update(test_params)
# your code here
else
# your code here
end
这是按设计工作的。例如,update
控制器方法通常如下所示:
def update
@test = Test.find(params[:id])
if @test.update(test_attributes)
# redirect to index with success messsage
else
render :edit
end
private
def test_attributes
# params.require here
end
end
然后,render :edit
将重新显示表单,其中包含错误消息和填写的错误值供用户更正。因此,您实际上确实希望模型实例中提供不正确的值。