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.