lurker
lurker

Reputation: 58244

Rails implicit validation error on existence of association

I'm running Rails 6.0.2.1.

I have a fairly simple model between clients, invoices, and proposals. An invoice belongs to a proposal (but this is optional - an invoice doesn't have to be based upon a proposal). Also, an invoice belongs to a client.

class Invoice < ActiveRecord::Base
  belongs_to :client
  belongs_to :proposal, foreign_key: "prop_id"
  ...

class Proposal < ActiveRecord::Base
  belongs_to :client
  has_one :invoice, foreign_key: "prop_id", dependent: :destroy
  ...

class Client < ActiveRecord::Base
  has_many :proposals, dependent: :destroy
  has_many :invoices, dependent: :destroy
  ...

These models have no validations between one another. That is, I do not have a validation indicating that an invoice must have a proposal or even a client. However, Rails is giving me validation errors on their existence if I check the validity of any field in the invoice:

> inv = Invoice.new
=> #<Invoice id: nil, client_id: nil, prop_id: nil, tocb_id: nil, fromcb_id: nil,
date_invoice: "2020-02-10", written_by: nil, terms: nil, date_due: nil,
status: "Pending", shipping: nil, amount: 0.0, amt_due: 0.0, deposit: nil,
tax_rate: nil, comments: nil>
> inv.errors.count
=> 0
> inv.valid? :amount
=> false
> inv.errors.count
=> 2
> inv.errors
=> #<ActiveModel::Errors:0x000056466dac7a38 @base=#<Invoice id: nil, client_id: nil,
prop_id: nil, ... , @messages={:client=>["must exist"], :proposal=>["must exist"]},
@details={:client=>[{:error=>:blank}], :proposal=>[{:error=>:blank}]}>

Why is it flagging missing client and missing proposal as existence errors?

Upvotes: 1

Views: 577

Answers (1)

Gautam
Gautam

Reputation: 1812

You are getting the error because in Rails 5 and above, whenever we define a belongs_to association, it is required to have the associated record present by default.

So you need to link a client and a proposal to invoice, only then would you be able to create an invoice object. Which means you need to do this -

client = Client.create
proposal = Proposal.create
inv = Invoice.new(client: client, proposal: proposal)

You can also mention the belongs_to relationship as optional, then the presence of the associated object won't be validated

class Invoice < ActiveRecord::Base
  belongs_to :client, optional: true
  belongs_to :proposal, foreign_key: "prop_id", optional: true
  ...

With optional: true

inv = Invoice.new

will not give any errors

Upvotes: 3

Related Questions