Don't validate belongs_to, or else...
Over on rails-core, I posted Edge Rails fails saving parent when has_many child ?.
The models I am using are:
1 2 3 4 5 |
class Invoice < ActiveRecord::Base belongs_to :customer has_many :lines, :class_name => 'InvoiceItem' validates_presence_of :no, :customer end |
1 2 3 4 |
class InvoiceItem < ActiveRecord::Base belongs_to :invoice validates_presence_of :invoice end |
As is, it was impossible to use the normal build and save idiom:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ ruby script\console Loading development environment. >> invoice = Invoice.new => #<Invoice:0x3a76060 ...> >> line = invoice.lines.build => #<InvoiceItem:0x3a5d610 ...> >> invoice.save! ActiveRecord::RecordInvalid: Validation failed: Lines is invalid from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/validations.rb:736:in `save!' from (irb):3 >> puts line.errors.full_messages Invoice can't be blank |
Well, of course. I know invoice can’t be blank. If I remove :invoice from the validates_presence_of, things work out fine:
1 2 3 4 5 6 7 8 9 10 |
... >> invoice.save! => nil >> invoice.new_record? => false >> line.new_record? => false >> line.invoice => nil >> # Huh? |
Digging into the code, ActiveRecord::Associations::AssociationProxy#set_belongs_to_association_for it turns out that only the foreign key is assigned to the child instance, not the full parent model. The behavior as seen above is therefore “normal”.
Turns out that if I validate the foreign key instead, things work perfectly:
1 2 3 4 |
class InvoiceItem < ActiveRecord::Base belongs_to :invoice validates_presence_of :invoice_id end |
1 2 3 4 5 6 7 |
... >> invoice.save! => nil >> line.new_record? => false >> line.invoice => #<Invoice:0x39fbea8 ...> |
Lesson learned: don’t validate the presence of the associated model, only it’s foreign key.