Except that’s not completely accurate. The presence already special cases a 
check to make sure the object was not marked for deletion. The reasoning in 
the docs:

adding the check to see that an associated object is not marked for 
destruction. This prevents the parent object from validating successfully 
and saving, which then deletes the associated object, thus putting the 
parent object into an invalid state

To me, this reasoning should also not apply to a *new, but invalid, 
associated object*. Rails already attempts attempt to persist the 
associated object. By the same reasoning as above, it should then make sure 
that object was actually persisted.

I think the underlying issue here behavior for a “new” versus “existing” 
record. The need for ‘validate :associated’ is a red herring. I agree, that 
for already persisted objects the existing Rails behavior of not validating 
it is correct. I find it extremely surprising behavior for a new 
non-persisted object.

Perhaps a simple persisted? check is all that is necessary in the logic?

On Thursday, September 11, 2014 7:24:20 PM UTC-4, Carlos Antonio da Silva 
wrote:

Well, the presence validation does exactly what it says, no matter which 
> type of attribute we're talking about. For example, it doesn't work with 
> booleans because it'd never allow `false` to be saved.
>
> To actually validate that the associated object is valid itself, you 
> should add the association validation, or add the validate option to the 
> belongs_to call, which will validate the associated object when saving the 
> parent.
>
> Hope that helps :)
>
> On Wed, Sep 10, 2014 at 10:47 PM, Aaron Kromer <aaron....@gmail.com 
> <javascript:>> wrote:
>
>> This behavior has been around for awhile (since at least 3.2.x; I didn't 
>> test further back). I'm a bit surprised this didn't trip my up before, but 
>> I'm sure it did and I just took the quick way out.
>>
>> To set some groundwork here is what the Active Record Validations Guide 
>> <http://guides.rubyonrails.org/active_record_validations.html#presence> 
>> presence validation (emphasis mine):
>>  
>>
>>> If you want to be sure that an association is present, you'll need to 
>>> test whether the associated object itself is present, and not the foreign 
>>> key used to map the association. 
>>
>>
>>> class LineItem < ActiveRecord::Base
>>>  belongs_to :order
>>>  validates :order, presence: true
>>> end
>>>
>>> *In order to validate associated records whose presence is required, you 
>>> must specify the :inverse_of option for the association*: 
>>
>>
>>> class Order < ActiveRecord::Base
>>>   has_many :line_items, inverse_of: :order
>>> end
>>>
>>> If you validate the presence of an object associated via a has_one or 
>>> has_many relationship, it will check that the object is neither blank? nor 
>>> marked_for_destruction?.
>>
>>
>> However, using inverse_of doesn't work. Note that this section of docs 
>> states that only has_one and has_many relationships will check only blank? 
>> and marked_for_destruction?.
>>
>> Compare this with the API docs for the same validation 
>> <http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_presence_of>
>> :
>>
>> Validates that the specified attributes are not blank (as defined by 
>>> Object#blank?), and, if the attribute is an association, that the 
>>> associated object is not marked for destruction. Happens by default on save.
>>
>>
>> That gives the indication that it doesn't seem to matter if you use 
>> inverse_of or not. Nor, will the presence validator ever validate the 
>> association. However, this causes some very odd behavior. Namely, it's 
>> possibly to save a record which, when pulled immediately back out of the 
>> database is not valid. This is because a null value has been saved in the 
>> place of the association's reference id.
>>
>> Here's code which demonstrates this:
>>
>> # Activate the gem you are reporting the issue against.
>> gem 'activerecord', '4.1.6.rc2'
>> require 'active_record'
>> require 'minitest/autorun'
>> require 'logger'
>>
>>
>> # Ensure backward compatibility with Minitest 4
>> Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
>>
>>
>> # This connection will do for database-independent bug reports.
>> ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 
>> ':memory:')
>> ActiveRecord::Base.logger = Logger.new(STDOUT)
>>
>>
>> ActiveRecord::Schema.define do
>>   create_table :posts do |t|
>>     t.string :name
>>   end
>>
>>
>>   create_table :comments do |t|
>>     t.integer :post_id
>>   end
>> end
>>
>> class Post < ActiveRecord::Base
>>   has_many :comments, inverse_of: :post
>>   validates_presence_of :name
>> end
>>
>> class Comment < ActiveRecord::Base
>>   belongs_to :post, inverse_of: :comments
>>   validates_presence_of :post
>> end
>>
>> class BugTest < Minitest::Test
>>   def setup
>>     Post.delete_all
>>     Comment.delete_all
>>   end
>>
>>   def test_association_persists_valid_objects
>>     post = Post.new(name: "Demoing confusing behavior")
>>     assert post.valid?
>>     refute post.persisted?
>>
>>     comment = Comment.create(post: post)
>>     assert comment.valid?
>>     assert comment.persisted?
>>     assert post.persisted?
>>     assert_equal 1, Post.count
>>     assert_equal 1, Comment.count
>>   end
>>
>>
>>   def test_valid_present_belongs_to_association
>>     post = Post.new
>>     refute post.valid?, "Post was valid but should be invalid"
>>
>>     Comment.create post: post
>>     assert_equal 0, Comment.count, "Comment was saved with invalid Post"
>>   end
>>
>>
>>   def test_showing_why_presence_should_mean_valid
>>     comment = Comment.create(post: Post.new)
>>
>>     if (persisted_comment = Comment.first)
>>       assert_equal comment, persisted_comment, "Assigned and persisted 
>> comments are different"
>>       assert persisted_comment.valid?, "Persisted comment isn't valid"
>>     else
>>       refute comment.valid?, "Assigned comment is valid"
>>     end
>>   end
>> end
>>
>>
>> Searches online seem to just take this as by design. Yet, it doesn't 
>> really make sense. The suggestions to "workaround" this are to either set 
>> the presence validation on the reference id field or adding an association 
>> validation.
>>
>> Setting the presence validation on the reference id field has the 
>> downside that it doesn't verify that the associated model object exists 
>> (without foreign keys defined in the database). The alternative is to use 
>> both presence and associated validations:
>>
>> class Comment < ActiveRecord::Base
>>   belongs_to :post, inverse_of: :comments
>>   validates :post, presence: true, associated: true
>> end
>>
>>
>> Am I completely missing something with this behavior?
>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "Ruby on Rails: Core" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to rubyonrails-co...@googlegroups.com <javascript:>.
>> To post to this group, send email to rubyonra...@googlegroups.com 
>> <javascript:>.
>> Visit this group at http://groups.google.com/group/rubyonrails-core.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
>
> -- 
> At.
> Carlos Antonio 
>
​

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rubyonrails-core+unsubscr...@googlegroups.com.
To post to this group, send email to rubyonrails-core@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.

Reply via email to