Peter Marklund

Peter Marklund's Home

Fri Mar 02 2007 04:17:21 GMT+0000 (Coordinated Universal Time)

Rails Gotcha: ActiveRecord::Base#update_attribute skips validation

It's something others have been bitten by. Although we should know better (why didn't we read the API docs carefully?) we naivley thought we could safely update a single attribute with the update_attribute method. When you invoke my_precious_object.update_attribute(:important_attribute, erroneous_value) then Rails will happily skip validations and save all attributes to the database. This is unlike the sister method update_attributes which updates several attributes and does use validation. I'm sure validation is skipped for a reason but I'm not sure what that reason is. In particular I'm not sure if that reason is strong enough to warrant the inconsistency and the risk of skipping validations.

I for one thought all public methods that create and update an ActiveRecord object were supposed to use validations. That would have seemed natural and even essential. Especially since most Rails apps run on MySQL which doesn't have a great track record when it comes to data integrity.

As far as I can see there is no mention in the Rails book of this validation glitch. I think I should pass a note to Dave Thomas about that.

If you look under the hood of Rails, it turns out update_attribute is equivalent to:

  object.attribute = value
  object.save(false)

The update_attribute method is first defined in the Base class and then overridden by the mixed in Validations module. To see the definitions of the save, update_attribute, and update_attributes methods you would be looking in vain in the Base class, in fact, the code you see there is never used, which feels a little bizarre. Fortunately there are comments in there pointing us to the Validations module.

On a side note, notice that unlike in Java, there is no method overloading on argument signatures in Ruby, so a method is identified solely by its name. When you redefine a method you can effectively change its argument list if you like, although this is probably not a good idea in most cases. The Validations module uses this ability though for the save method to add an optional boolean argument that determines if validation should be performed or not. This in turn is what the redefined update_attribute method makes use of. Questions? :-)