Why, again, is it ActiveRecord::Base#update_attribute doesn't trigger validations?

Hi!
I was hoping someone could take the time to explain to me why update_attribute doesn’t trigger validations? Why am I concerned with this?

  1. It’s inconsistent with the behaviour of the update_attributes method

  2. People may think (like me) that all public methods that update ActiveRecord objects use validations and thus risk ending up with inconsistent data. The principle of least surprise here would suggest that the method does trigger validations. The non-validating behaviour of update_attribute is also not documented by the Rails book.

For some background, see:

http://dev.rubyonrails.org/ticket/7698
http://marklunds.com/articles/one/333

Cheers

Peter

I was hoping someone could take the time to explain to me why
update_attribute doesn't trigger validations? Why am I concerned with this?

Because it's intended to be used in scenarios where you're not
interested in dealing with validation errors. Such as automatic
functions that update a flag or increment a counter at billing. These
functions should not fail just because the object is invalid for
whatever reason.

If you want the same functionality with validation, you can use
update_attributes :attribute => value.

If this is confusion, please do add a documentation patch explaining
it fully.

Confusion probably arises because both methods have such similar
names. Perhaps update and update_without_validation, both taking a
hash of attributes, would make a clearer API? I appreciate that the
way rails is written, implementing this could be more complicated than
a simple rename method, but if it makes ActiveRecord easier to use it
could be worth it.

Tom

I think things should be swapped around, the API should be designed
for the common case which is that you want validation. If you don't
want validation, then do

object.attribute = value
object.save(false)

Of course, this is ultimately a matter of taste and of priorities, but
I think in this case data integrity and the principle of least
surprise should be prioritized.

I didn't know about this either, and I've read through AR almost in
its entirety. The similar naming of the methods, to me, strongly
implies that they have similar behavior and that the only difference
is that one updates one attribute, the other updates any number of
attributes.

I'm curious as to whether anyone currently depends on
update_attribute's lack of validation and, if so, how should we phase
it into a validating method? One possible way to do it is that if
validation fails, issue a warning stating that the implementation has
changed and that if you want to update without validation to use
attributes= and save(false). The problem with this, of course, is that
people who expect update_attribute to validate will be receiving
spurious warnings. Other suggestions?

-bd

Lots of people are depending on the current behavior. The method wasn't added arbitrarily, or in a fit of spite, it was extracted from real use, just like the rest of Rails. I use it all the time as a "update this flag in spite of the validations" scenario.

I do understand that the naming can be confusing, but it's pretty late in the game to be changing it. It's used as a way to skip validations even inside Rails itself, and changing the behavior of that method would have very far-reaching implications.

- Jamis

eventualbuddha:

I'm curious as to whether anyone currently depends on
update_attribute's lack of validation and, if so, how should we phase
it into a validating method?

Jamis Buck:

Lots of people are depending on the current behavior. [...]
I do understand that the naming can be confusing, but it's pretty late in the game to be changing it.

Those new to the game continue to arrive, though. Suggestion: introduce a method with the same behaviour as update_attribute and a better name. Leave the old method unchanged, but deprecate it.

  --Anthony.

Those new to the game continue to arrive, though. Suggestion: introduce
a method with the same behaviour as update_attribute and a better name.
Leave the old method unchanged, but deprecate it.

I really don't think deprecating update_attribute is justified,
rewording the documentation to indicate you probably don't want to use
it would be much easier for all concerned.

I'll whack that in one of my AR documentation patches that is coming
soon. Although the clarification is there, it's not wildly obvious.

Cheers,
Jamie van Dyke
Fear of Fish

jamie@fearoffish.co.uk
+44 (0)7816 519649

Yeah, I understand that changing at this point can be problematic, but
maybe it's still worthwhile in the long run. To me, the clearest
syntax would be something like

object.update_attribute(:attribute, value, :validate => false)

Either way, I think I understand now the use case that this method
comes from now, and I've actually had it myself. In my case it was a
scheduled procedure that needed to update a single flag or date
column. What I ended up doing was hand writing my own method called
update_column which only updated that single column. The reason I
didn't use update_attribute was that it updates *all* columns which
lead to locking/concurrency issues. As far as I can tell, the ideal
implementation of this use case is a method that updates only the
column that you are interested in, but that *does* in fact trigger
validation but only validation for that particular column.

I have yet to come across a use case where I need to set a column to a
value that by itself is invalid (i.e. setting a date to something that
is not a date, setting a string that can only have an enumeration of
values to something else etc.). It's a different issue that the set of
values for all attributes should be allowed to be invalid which I
think is what DHH and Jamis are referring to.

I think in this case data integrity and the principle of least
surprise should be prioritized.

I appreciate your passion for having a clear API, but you might not
want to use PoLS as a way of supporting your argument. Among other
people, Matz has distanced himself from this saying as it holds an
assumption of universality that is rarely, if ever, present.

The current behavior conforms with PoLS for me. It doesn't for you.
Thus, there is no single PoLS for the issue, making the argument moot.

Again, I do appreciate caring about the details. And it's great to see
enthusiasm for improving Rails on all fronts. But I think we can get
higher yields elsewhere after grabbing the low-hanging fruit of
updating the documentation.

I said earlier that I understood the use case that made
update_attribute be non-validating, but as I've thought about this
more I'm not so sure anymore. My interpretation of what you are saying
is that you have data in your database that doesn't validate, and you
don't want your flag updating script to be stopped by this. So for
whatever reason the data in your database and your validation code is
out of sync. I'm not sure why this would happen, but I suppose that if
it does happen it's typically a) something you want to be aware of,
and b) something you want to correct as soon as possible, since any
scheduled scripts that does use validation will be unable to save.
Given all this I guess I might want my flag updating script to
validate before it saves (so that I can be alerted of validation
issues), and I might not, it depends on the situation.

I think there is general agreement that the naming of update_attribute
is unfortunate since it doesn't in any way suggest why it isn't
validating when the plural form of the method is. I haven't been
following the discussions on the PoLS, but I think it's pretty obvious
that it has always been a guiding principle in API design, even before
the term was coined, and always will be. I think both Ruby and Rails
make a great job following PoLS and that's one of the main reasons
they are such a joy to use. An API that surprises you again and again
will just be too frustrating and hard to use. It's common sense.

I'm not the only one who has been surprised. Dave Thomas didn't know
update_attribute is non-validating, which is why it's not in the book,
and his spontaneous reaction when I told him about it was that it's
probably a bug. I think that's what gave me the courage to bring up
the issue here.

I'm not on a one man crusade here to change the API. I accept the API
won't change at this point, and no API will ever be perfect to
everybody, and that's ok.

I sincerely appreciate all the time you have put into clearifying
things to me. This discussion has definitely given me some new
insights. Thanks guys!

My apologies for the necro-post, just wanted to express one concern
with the solution proposed here.

If attr_protected is in use for the attribute in question,
`update_attributes :attr => val` won't function just as if we used the
singular update_attribute.

Obviously this is not a huge issue as we could just do something like
`model.attr = val \ model.save`, but that's a bit chunky.

It would be nice to have a way to use `update_attribute` WITH
validations. I wouldn't propose, nor agree with, changing the default
behavior of `update_attribute` at this stage. I'm just saying it would
be nice to have validations as an option for that method, or at least
another method that could update a single protected attribute with
validations.

Thanks for listening.

update_attribute_without_validation_skipping

The original update_attribute does perform validations. It is
overriden with alias_method_chain by validations.rb to skip 'em. The
original method is then preserved with the above name.

Thanks, Chris. No more 3AM posting from me.

Just use update_attributes(name => value)

Kind regards,
Thijs

He's updating an attribute that's either not allowed by
attr_accessible, or it's protected by attr_protected, so a mass
assignment won't work.

What about update_attribute name, value, true ?

I realize this issue has sort of been laid to rest but I just wanted
to chime in and raise the viewpoint that Peter's description of his
custom method "update_column" is really what a method named
"update_attribute" should be doing as opposed to updating all the
columns.

Regardless of whether or not validation should be called the current
behavior adversely impacts save situations that involve concurrency
and isn't of any added value for save situations that don't. Perhaps
I'm missing legitimate reasons for this behavior? I too am facing the
same situation as Peter. Wishing to update a flag that isn't user
editable in a table that contains other user editable data. I'm going
to write the same custom method as Peter has but again looking at the
name "update_attribute" and knowing what it does in practice feels a
little disingenuous :slight_smile:

Best regards,

-Michael
http://javathehutt.blogspot.com

I'd like to add something, and I'll include some potentially useful code
so hopefully my two cents is 'paid for'.

You've given example use cases in which you need a record in the
database to be 'invalid', temporarily. Is it possible that your
validations not covering a particular (exceptional) case? For example,
suppose a field is required EXCEPT in a certain condition. One case
that comes to mind is when the record represents a document that a user
can save in an incomplete state, returning later to complete it. It
might be allowable for some fields to be nil when the document is
incomplete.

Using update_attribute to accommodate this seems, to me, to discourage a
full elaboration of validations. This is particularly troubling to me
for two reasons:

1) Rails takes a dim view of database constraints, so model probably
bears more responsibility for overall system integrity than it would in
other frameworks.

2) Rails (rightly) insists on MVC, yet methods like update_attribute
diminish one of the best things about the 'M' - the domain-driven
approach in which 'model' really means 'a model of the business/domain'.
I would prefer to see an option on Rails, perhaps
config.strict_validation, that could be used by those of us who want to
be able to rely on the model classes in this way.

So, if config.strict_validation == true, we could run the following on
startup:

# Adds update_attribute!, which incurs validation, and
# issue warnings for use of methods that either bypass or
# don't throw exceptions on validation failure.
module ActiveRecord
  class Base

    # Incurs validation.
    def update_attribute!(name, value)
      send(name.to_s + '=', value)
      save!
    end

    def update_attribute_with_warning(name, value)
      ActiveRecord::Base.validation_warning(:update_attribute)
      update_attribute_without_warning(name, value)
    end

    alias_method_chain :update_attribute, :warning

    private

    # Produce a warning encourage update_attribute! use
    # and listing the line from which it was called.
    def self.validation_warning(method_name)
      # Don't warn about calls from this file or active_record...
      unless caller[1] =~ /activerecord/
        msg=<<EOM
warning: The method '#{method_name}' bypasses validation. Consider
'#{method_name}!'.
#{method_name} was called from #{caller[1]}
EOM
        warn msg
      end
    end
  end
end

In my own project, I also warn on save, update_attributes and create.

Thanks,

Brian Hartin
brian.hartin@(The free email system by Google).com