I have two Rails model namely Invoice and Invoice_details. An Invoice_details belongs to Invoice, and an Invoice has many Invoice_details. I'm not able to save Invoice_details via Invoice model using accepts_nested_attributes_for in Invoice.
I get the following error :
(0.2ms) BEGIN
(0.2ms) ROLLBACK
Completed 422 Unprocessable Entity in 25ms (ActiveRecord: 4.0ms)
ActiveRecord::RecordInvalid (Validation failed: Invoice details invoice must exist):
app/controllers/api/v1/invoices_controller.rb:17:in `create'
Below is the code snippet for invoice.rb
:
class Invoice < ApplicationRecord
has_many :invoice_details
accepts_nested_attributes_for :invoice_details
end
The code snippet for invoice_details.rb
:
class InvoiceDetail < ApplicationRecord
belongs_to :invoice
end
The Controller
code :
class Api::V1::InvoicesController < ApplicationController
respond_to :json
def index
comp_id = params[:comp_id]
if comp_id
invoices = Invoice.where(:company_id => comp_id)
render json: invoices, status: 201
else
render json: { errors: "Company ID is NULL" }, status: 422
end
end
def create
Rails.logger.debug invoice_params.inspect
invoice = Invoice.new(invoice_params)
if invoice.save!
render json: invoice, status: 201
else
render json: { errors: invoice.errors }, status: 422
end
end
def invoice_params
params.require(:invoice).permit(:total_amount,:balance_amount, :customer_id, invoice_details_attributes:[:product_id])
end
end
The raw JSON data passed to the controller :
{
"invoice":{
"total_amount":"100",
"balance_amount":"0",
"customer_id":"1",
"invoice_details_attributes":[{
"product_id":"4"
}]
}
}
invoice_details schema
|id | invoice_id | product_id | created_at | updated_at|
invoice schema
|id| total_amount |balance_amount | generation_date | created_at | updated_at | customers_id|
ActiveRecord::RecordInvalid (Validation failed: Invoice details invoice must exist):
The error is due to you are not permitting invoice_id
while saving invoice_detail
for invoice
In Rails 5, the presence of associated object will be validated by default. You can bypass this validation by setting optional :true
From the Guides
If you set the :optional option to true, then the presence of the associated object won't be validated. By default, this option is set to false.
Solution:
Either permit invoice_id
in invoice_details_attributes
of invoice_params
def invoice_params
params.require(:invoice).permit(:total_amount,:balance_amount, :customer_id, invoice_details_attributes: [:product_id, :invoice_id])
end
OR
If you wish not to, then set optional :true
class InvoiceDetail < ApplicationRecord
belongs_to :invoice, optional :true
end
I still don't know the reason why the above thing was not working however when I explicitly declared a bi-directional relation between the two models by using inverse_of
.
class Invoice < ApplicationRecord
has_many :invoiceDetails, inverse_of: :invoice
accepts_nested_attributes_for :invoiceDetails
end
It seemed Rails was not setting the invoice attribute on the invoice_details before attempting to save it, triggering the validation errors. This is a bit surprising as other has_many relations should have the same save mechanics and were working just fine.
After a bit of googling I found few posts on this behaviour occurring in the older version of Rails, I don't know if that still exists in the new version. Further can be looked in these links :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With