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.kro...@gmail.com>
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-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.
>



-- 
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