9 June 2009

Updating attributes in Rails

When I save data in Rails, I continually misuse the different methods available, so that my code is sprinkled with save, save!, save(false), update_attribute, update_attributes and so on. I also often introduce bugs by assigning a new value to an attribute and then forgetting to update it.

So, here is a quick summary.

save is the one to use if I want my model to validate. And, by the way, it is well worth using validations, like validates_presence_of, to check that an assignment hasn’t been forgotten somewhere. save is used most commonly in the create method of a controller where errors are likely to be handled by the view code.

save! works like save except that it raises an ActiveRecord::RecordInvalid exception suitable for use in a rescue clause. In other words, the only context in which to use save! is this one:


begin
  thing.save!
rescue RecordInvalid => error
  # do something
end

It is likely to be used outside the controller (i.e. in the model) where I should want to do something sensible if any validations fail.

save(false), on the other hand, provides no validations at all and should only be used if I am absolutely sure about the data I am saving or perhaps if I am saving several records together and want to speed things up a bit (and am feeling reckless).

update_attributes() and update_attributes!() behave just like save and save! respectively, but accept a hash of attributes as an argument so allow assignments to multiple attributes and saving all in a single line. update_attributes is most commonly used when updating a model from within a controller.

update_attribute(), as the name implies, just updates a single attribute and bypasses validations; a sensible choice if a single attribute—such as a boolean flag on an existing record—is being updated.

Note that behind the scenes update_attribute uses save(false), so that it is not a great idea to use it if I am likely to have other attributes awaiting update.

Incidentally, if I am updating a boolean and know that I just want its state to change, then it might be worth using toggle(name) instead of update_attribute(name, value), which might save a line or two of code.

Also, update(id, attributes) combines the reading of a row and the updating of its attributes onto a single line, so that instead of:

thing = Thing.find(id)
thing.update_attributes(attributes)

I can have:

Thing.update(id, attributes)

Which is also the same as:

Thing.find(id).update_attributes(attributes)

So to summarize:

Use validations where I can;

Only use save(false) for performance reasons, not to be lazy;

Beware of using update_attribute in any context where more than one attribute is being updated;

Outside controllers, use the bang! versions of save and update_attributes and always inside a block.

d. sofer