When to use "validates_associated" v "validate => true"
It seems that in rail开发者_运维技巧s 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?
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
.
精彩评论