For example, if I run test.update_attributes prop1: 'test', prop2: 'test2'
when prop1
and prop2
have validations that prevent those values, test.prop1
will still be 'test'
and test.prop2
will still be 'test2'
. Why is this happening and how can I fix it?
According to the Rails docs for update_attributes
, it's an alias of update
. Its source is as follows:
# 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
So, it's wrapped in a DB transaction which is why the rollback happens. However, let's check out assign_attributes
. According to its source:
# File activerecord/lib/active_record/attribute_assignment.rb, line 23
def assign_attributes(new_attributes)
...
_assign_attribute(k, v)
...
end
That is defined as:
# 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
So, when you call test.update_attributes prop1: 'test', prop2: 'test'
, it basically boils down to:
test.prop1 = 'test'
test.prop2 = 'test'
test.save
If save
fails the validations, our in-memory copy of test
still has the modified prop1
and prop2
values. Hence, we need to use test.reload
and the issue is resolved (i.e. our DB and in-memory versions are both unchanged).
tl;dr Use test.reload
after the failed update_attributes
call.