I have a basic invoice setup with models: Invoice, Item, LineItems.
# invoice.rb
class Invoice < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
validates_presence_of :status
before_save :default_values
def default_values
self.status = 'sent' unless self.status
end
end
# item.rb
class Item < ActiveRecord::Base
has_many :line_items
validates_presence_of :name, :price
end
# line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :item
belongs_to :invoice
before_save :default_values
validates_presence_of :invoice_id
validates :item_id, :presence => true
end
There is more in the model but I only presented the above for simplicity.
I get the following errors:
2 errors prohibited this invoice from being saved:
Line items invoice can't be blank
Status can't be blank
So two problems:
-
If I remove
validates :invoice_id, :presence => trueI don’t get theLine items invoice can't be blankerror message anymore, but why? I do want to validate the invoice_id on line_items, ALL line_items are supposed to have an invoice_id. How can I validate the invoice_id on line_items without getting an error? -
Why am I getting the
Status can't be blankerror if I set it as a default value? I can probably set it up on the invoices_controller but I think the default value should be set in the model, right? How can I validate the presence of status and still have a default value in the model for it?
Both of these validation errors are occurring because validations get called before save (and before the
before_savecallback).I’m assuming that you’re using a nested_form to create the invoice and it’s line items at the same time. If this is the case, you don’t want to
validates :invoice_id, :presence => trueon the line items – the invoice and the line items are coming in at the same time, and the invoice hasn’t been saved yet, so it doesn’t have an id. If you leave the validation in, you’ll need to create and save an empty invoice first, and then create the line items later so the invoice_id is available. If you only want to make sure invoice_id is still set after any edits, you can enforce this viavalidates :invoice_id, :presence => true, :on => :updatethis will skip the validation when the line item is being created (and the invoice_id isn’t available yet).You’re running into problems with
validates :status, :presence => truefor similar reasons – the values coming in via the request are being validated against, and the “status” value isn’t there. Thebefore_savecallback runs after validation. You can set the default value in thebefore_validationorafter_initializationcallback and the values will be there when validations are run.Check out the Callbacks documentation for Rails for more info.