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