Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Validates_presence_of associated object marked for destruction #6812
We've come across an issue with validates_presence_of on an associated object that is marked for destruction. We have model Man, that has one Face, with validates_presence_of :face. If the face is marked for destruction, however, then the man still passes validations. This then allows the face to be destroyed, without invalidating the parent or adding an error until reload. The same principle applies to a has_many relationship.
Here are a couple failing tests (within ActiveRecord):
require "cases/helper" require 'models/topic' require 'models/reply' require 'models/man' require 'models/face' class AssociationValidationTest < ActiveRecord::TestCase def test_validates_presence_of_has_one_marked_for_destruction Man.validates_presence_of(:face) m = Man.new f = Face.new m.face = f assert m.valid? f.mark_for_destruction assert m.invalid? end def test_validates_presence_of_has_many_marked_for_destruction Topic.validates_presence_of(:replies) t = Topic.new t.replies << [r1 = Reply.new, r2 = Reply.new] assert t.valid? r1.mark_for_destruction assert t.valid? r2.mark_for_destruction assert t.invalid? end end
Possible solutions for this include:
We think that option 1 is the best fix, but we wanted to get some feedback from a wider audience about the preferred solution.
Nick, Evan and Brent
the comment of
# This does _not_ actually_not_ destroy the record instantly, # rather child record will be destroyed when <tt>parent.save</tt> is called.
def test_validates_presence_of_has_one_marked_for_destruction Man.validates_presence_of(:face) m = Man.new f = Face.new m.face = f assert m.valid? f.mark_for_destruction m.save assert m.invalid? end
let me know if it not fail, thanks!
ok I have tested this and it seems has a strange behaviour but maybe is better a test like the following:
def test_validates_presence_of_has_one_marked_for_destruction Man.validates_presence_of(:face) m = Man.new f = Face.new m.face = f assert m.valid? f.mark_for_destruction assert !m.save end
I can see a possible problem with the proposed changes in Active Model, 1 and 3, mainly because validations or errors are completely unaware of Active Record associations, they only test if the given object is actually present or not. This means that they have no context to ask about
Option 2 may seem ok, with the exception that there's a great chance that it'd break a lot of existing functionality, so I'd vote against it at first thought.
I can't say if Active Record should have a special kind of validation for that, but I can think of a simple working solution:
class Man has_one :face validates_presence_of :face_not_marked_for_destruction private def face_not_marked_for_destruction face if face && !face.marked_for_destruction? end end class Topic has_many :replies validates_presence_of :replies_not_marked_for_destruction private def replies_not_marked_for_destruction replies.reject(&:marked_for_destruction?) end end
The 2) approach seems controversial, at least, since objects technically are there, they will be removed on next save.
For me the simplest approach would be to subclass
referenced this issue
Jun 22, 2012
pushed a commit
Nov 2, 2013
Maybe I'm not understanding this issue correctly, but to me it means that when you validate the presence of an associated object, it should not be possible to destroy that associated object. If that's the case, then it seems like this is still an issue in Rails 4.0.4.
If I have these 2 models associated like so:
class Location < ActiveRecord::Base has_one :office validates_presence_of :office end class Office < ActiveRecord::Base belongs_to :location end
Now let's say a location
I would expect an error to be raised because a Location must have an Office.
What am I missing here?