How to validate presence of associated?

123 views
Skip to first unread message

Christian Rishøj

unread,
Sep 24, 2008, 8:34:29 AM9/24/08
to Ruby on Rails: Talk

On Rails 2.1.1, I have this simple case:

class Post < ActiveRecord::Base
has_many :comments
end

class Comment < ActiveRecord::Base
belongs_to :post
validates_presence_of :post_id
end

Now, when building associated comments to an unsaved post, the
validation fails:

p = Post.new
=> #<Post id: nil, name: nil, created_at: nil, updated_at: nil>

c = p.comments.build
=> #<Comment id: nil, text: nil, comment_id: nil, created_at: nil,
updated_at: nil>

c.valid?
=> false

c.errors
=> #<ActiveRecord::Errors:0x23f8d18 @base=#<Comment id: nil, text:
nil, comment_id: nil, created_at: nil, updated_at: nil>,
@errors={"post_id"=>["can't be blank"]}>

According to http://dev.rubyonrails.org/ticket/4147,
validates_presence_of the foreign key is the proper way to validate
the presence associated objects. However, the documentation warning
regarding this was later removed.

So, question is: How do I validate the presence of associated objects,
while still allowing the building of associated objects to an unsaved
model?

Regards,

Christian

Mark James

unread,
Sep 24, 2008, 8:52:49 AM9/24/08
to rubyonra...@googlegroups.com

Christian, this comes up quite often. I'm glad the incorrect
warning has been removed from the API, but I would dispute the
validity of that old ticket [the blog post explaining the
ticket-creator's reasoning is no longer available].

Here's my most recent post on this topic:
http://groups.google.com/group/rubyonrails-talk/msg/3c01e9b320604dc8


--
Rails Wheels - Find Plugins, List & Sell Plugins - http://railswheels.com

Christian Rishøj

unread,
Sep 24, 2008, 2:22:02 PM9/24/08
to Ruby on Rails: Talk


On Sep 24, 2:52 pm, Mark James <m...@bigpond.net.au> wrote:

> > So, question is: How do I validate the presence of associated objects,
> > while still allowing the building of associated objects to an unsaved
> > model?
>
> Christian, this comes up quite often. I'm glad the incorrect
> warning has been removed from the API, but I would dispute the
> validity of that old ticket [the blog post explaining the
> ticket-creator's reasoning is no longer available].
>
> Here's my most recent post on this topic:http://groups.google.com/group/rubyonrails-talk/msg/3c01e9b320604dc8

Thank you for this, Mark.

I was able to make the association proxy set the target object on
unsaved associations with the following monkey patch:


module ActiveRecord
module Associations
class AssociationProxy
protected
#
# Allow validates_presence_of :target on belongs_to
associations
# before the target has been saved.
def set_belongs_to_association_for(record)
record["#{@reflection.options[:as]}_type"] =
@owner.class.base_class.name.to_s if @reflection.options[:as]
if @owner.new_record?
set_proxy_target_method = "set_#{@reflection.options[:as] ||
@reflection.primary_key_name.chomp('_id')}_target"
record.send(set_proxy_target_method, @owner)
elsif @reflection.options[:as]
record["#{@reflection.options[:as]}_id"] = @owner.id
else
record[@reflection.primary_key_name] = @owner.id
end
end
end
end
end

Now I can do:

p = Post.new
=> #<Post id: nil, name: nil, created_at: nil, updated_at: nil>

c = p.comments.build
=> #<Comment id: nil, text: nil, post_id: nil, created_at: nil,
updated_at: nil>

c.valid?
=> true

Now I wonder: Why doesn't ActiveRecord do this by default?

Christian

Christian Rishøj

unread,
Sep 24, 2008, 7:42:47 PM9/24/08
to Ruby on Rails: Talk


On Sep 24, 8:22 pm, Christian Rishøj <christ...@rishoj.net> wrote:
> On Sep 24, 2:52 pm, Mark James <m...@bigpond.net.au> wrote:
>
> > > So, question is: How do I validate the presence of associated objects,
> > > while still allowing the building of associated objects to an unsaved
> > > model?
>
> > Christian, this comes up quite often. I'm glad the incorrect
> > warning has been removed from the API, but I would dispute the
> > validity of that old ticket [the blog post explaining the
> > ticket-creator's reasoning is no longer available].
>
> > Here's my most recent post on this topic:http://groups.google.com/group/rubyonrails-talk/msg/3c01e9b320604dc8
>
> Thank you for this, Mark.
>
> I was able to make the association proxy set the target object on
> unsaved associations with the following monkey patch:
>
> module ActiveRecord
>   module Associations
>     class AssociationProxy
>       protected
>       #
>       # Allow validates_presence_of :target on belongs_to
> associations
>       # before the target has been saved.
>       def set_belongs_to_association_for(record)
>         record["#...@reflection.options[:as]}_type"] =
> @owner.class.base_class.name.to_s if @reflection.options[:as]
>         if @owner.new_record?
>           set_proxy_target_method = "set...@reflection.options[:as] ||
> @reflection.primary_key_name.chomp('_id')}_target"
>           record.send(set_proxy_target_method, @owner)
>         elsif @reflection.options[:as]
>           record["#...@reflection.options[:as]}_id"] = @owner.id
>         else
>           record[@reflection.primary_key_name] = @owner.id
>         end
>       end
>     end
>   end
> end
>
> Now I can do:
>
> p = Post.new
> => #<Post id: nil, name: nil, created_at: nil, updated_at: nil>
>
> c = p.comments.build
> => #<Comment id: nil, text: nil, post_id: nil, created_at: nil,
> updated_at: nil>
>
> c.valid?
> => true
>
> Now I wonder: Why doesn't ActiveRecord do this by default?

Seems I'm not the first to get this idea:
http://www.nabble.com/-set_belongs_to_association_for-doesn't---td11423337.html

Problem is that setting the (unsaved) owner object on an associated
object causes a lot of tests to break. Namely, validations go into a
recursion of e.g. validate_associated_records_for_developer and
validate_associated_records_for_audit_logs back and forth ad
infinitum.

This could really be working a lot better.

Any clues on how to deal with this?

Christian

Mark James

unread,
Sep 27, 2008, 6:54:35 AM9/27/08
to rubyonra...@googlegroups.com
Christian Rishøj wrote:

> Problem is that setting the (unsaved) owner object on an associated
> object causes a lot of tests to break. Namely, validations go into a
> recursion of e.g. validate_associated_records_for_developer and
> validate_associated_records_for_audit_logs back and forth ad
> infinitum.

I guess it's the tests that require fixing. And AR should be
detecting loopy validations and raising an error.

Reply all
Reply to author
Forward
0 new messages