Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use "validates_associated" v "validate => true"

It seems that in rails you can define association validations in two places, either on the association itself:

class Child    belongs_to :parent, :validate => true  end 

Or as a validation callback:

class Child    belongs_to :parent    validates_associated :parent  end 

What is the difference between these two methods?

Testing the difference

I thought that maybe the former creates a backpressure and enforces that the parent is only valid if the child is valid:

i.e. (when setting :validate => true)

child.valid? # => false child.parent.valid? # => also evaluates to false because of the :validate => true condition   # do whatever it takes to make the child valid again #... child.valid? # => true child.parent.valid? # => true 

However I tested it and this doesn't happen. So what's the difference (if any) between the two methods?

like image 975
Peter Nixey Avatar asked Jun 18 '11 11:06

Peter Nixey


1 Answers

I had to dig into the Rails (3.0.7) code to find some differences. The core functionality looks the same to me -- they both seem to call valid? on the associated record(s).

The key differences that I did find only appear when using the :autosave feature or when either destroying the associated object or marking it for destruction. For example, I have:

class AbsentDate < ActiveRecord::Base   belongs_to :user, :autosave => true, :validate => true end 

And I see the following behaviour:

user = User.new(:username => "Jimmy") user.valid?                               # => true ad = AbsentDate.new(:user => user) user.username = nil                           user.valid?                               # => false ad.valid?                                 # => false ad.errors.full_messages                   # => ["User username cannot be empty"] ad.user.mark_for_destruction ad.valid?                                 # => true 

Note that marking the user for destruction resulted in a valid AbsentDate. Also note that there is only one error message. Now consider this case:

class AbsentDate < ActiveRecord::Base   belongs_to :user, :autosave => true   validates_associated :user end 

This is what I see happening:

user = User.new(:username => "Jimmy") user.valid?                                # => true ad = AbsentDate.new(:user => user) user.username = nil user.valid?                                # => false ad.valid?                                  # => false ad.errors.full_messages                    # => ["User username cannot be empty", "User is invalid"] ad.user.mark_for_destruction ad.valid?                                  # => false 

Two error messages this time, and the AbsentDate is still false even though its user has been marked for destruction. I was able to replicate these results by calling destroy instead of mark_for_destruction.

One last thing: if you use validates_associated, you get several options (:if, :unless, :on, :message) that you won't have if you use the flag on belongs_to.

like image 166
Mike A. Avatar answered Sep 22 '22 02:09

Mike A.