Validates Presence Of Foreign Key
When reading the Rails documentation for validates_presence_of I eyed the warning pertaining to foreign keys.
I came across this article and when running the examples written, I found the same errors.
Stumped, I started crawling through the Rails code. I documented the behavior with this small rails app which includes the Rails 1.2.3 frozen in1.
Instead of using invoice
and line_items
, I decided to simply use parent
and child
Parent:
class Parent < ActiveRecord::Base
has_many :children
end
Child:
class Child < ActiveRecord::Base
belongs_to :parent
validates_presence_of :parent, :name
end
Invalid Code:
>> p = Parent.new(:name => "Dad")
=> #<Parent:0x23ac8f4 @attributes={"name"=>"Dad"}, @new_record=true>
>> c = p.children.build(:name => "Son")
=> #<Child:0x2367858 @attributes={"name"=>"Son", "parent_id"=>nil}, @new_record=true>
>> p.save!
ActiveRecord::RecordInvalid: Validation failed: Children is invalid
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/validations.rb:764:in `save_without_transactions!'
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:133:in `save!'
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:59:in `transaction'
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:95:in `transaction'
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:121:in `transaction'
from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:133:in `save!'
from (irb):3
Valid Code:
>> p = Parent.new(:name => "Dad")
=> #<Parent:0x22b9f28 @attributes={"name"=>"Dad"}, @new_record=true>
>> c = p.children.build(:name => "Son", :parent => p)
=> #<Child:0x229626c @attributes={"name"=>"Son", "parent_id"=>nil}, @parent=#<Parent:0x22b9f28 @attributes={"name"=>"Dad"}, @new_record=true, @children=[#<Child:0x229626c ...>]>, @new_record=true>
>> p.save!
=> true
What seems to be happening is the validations are called twice. The first time with the parent_id of nil, and then the second with the parent_id (obviously now it’s saved the Parent and is going to actually save the Child).
Perhaps later I will crawl through the Rails code and submit a patch to automate the setting of association variable, but I’m still debating if that’s the correct thing to do.
What gets really odd is if I take out the @validate_presence_of@ from the Child and use the first method, both Parent and Child get saved, and the Child has the foreign key set.
Parent:
class Parent < ActiveRecord::Base
has_many :children
end
Child:
class Child < ActiveRecord::Base
belongs_to :parent
validates_presence_of :name
end
Valid Code:
>> p = Parent.new(:name => "Dad")
=> #<Parent:0x23ac8f4 @attributes={"name"=>"Dad"}, @new_record=true>
>> c = p.children.build(:name => "Son")
=> #<Child:0x2368dfc @attributes={"name"=>"Son", "parent_id"=>nil}, @new_record=true>
>> p.save!
=> true
>> p.children
=> [#<Child:0x2368dfc @attributes={"name"=>"Son", "id"=>2, "parent_id"=>2}, @new_record=false, @errors=#<ActiveRecord::Errors:0x233fe84 @errors={}, @base=#<Child:0x2368dfc ...>>>]