update_attributes 即使验证失败也会更改属性
update_attributes changes attributes even if validation fails
例如,如果我 运行 test.update_attributes prop1: 'test', prop2: 'test2'
当 prop1
和 prop2
有防止这些值的验证时,test.prop1
仍然是 'test'
和 test.prop2
仍然是 'test2'
。为什么会发生这种情况,我该如何解决?
根据the Rails docs for update_attributes
, it's an alias of 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
。根据its source:
# 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
将重新显示带有错误消息的表单,并填写不正确的值以供用户更正。所以您实际上确实希望在模型实例中提供不正确的值。
例如,如果我 运行 test.update_attributes prop1: 'test', prop2: 'test2'
当 prop1
和 prop2
有防止这些值的验证时,test.prop1
仍然是 'test'
和 test.prop2
仍然是 'test2'
。为什么会发生这种情况,我该如何解决?
根据the Rails docs for update_attributes
, it's an alias of 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
。根据its source:
# 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
将重新显示带有错误消息的表单,并填写不正确的值以供用户更正。所以您实际上确实希望在模型实例中提供不正确的值。