[英]Rails “has_many” association: “collection=” doesn't work as expected
發票有許多發票分錄:
class Invoice < ActiveRecord::Base
has_many :invoice_entries, :autosave => true, :dependent => :destroy
validates_presence_of :date
end
class InvoiceEntry < ActiveRecord::Base
belongs_to :invoice
validates_presence_of :description
end
假設我們在數據庫中有一張發票:
id: 1
date: '2013-06-16'
它有兩個發票條目:
id: 10 id: 11
invoice_id: 1 invoice_id: 1
description: 'do A' description: 'do C'
現在,我有了新的發票條目:
id: 10
description: 'do B' description: 'do D'
(Existing invoice entry (New invoice entry
with updated description) without id)
我希望發票僅具有這些新發票條目(這意味着應刪除id=11
發票條目)。
invoice.invoice_entries = new_invoice_entries
似乎完成了一半的工作。 它刪除id=11
的發票條目,創建描述為'Do D'
的新發票條目, 但不會將id=10
的發票條目的描述從'Do A'
為'Do B'
。 我猜想當Rails在new_invoice_entries
看到一個現有的id
時,它將完全忽略它。 真的嗎? 如果是,這背后的理由是什么?
我的完整代碼如下。 您將如何解決此問題? (我使用Rails 4,以防它簡化了代碼。)
# PATCH/PUT /api/invoices/5
def update
@invoice = Invoice.find(params[:id])
errors = []
# Invoice entries
invoice_entries_params = params[:invoice_entries] || []
invoice_entries = []
for invoice_entry_params in invoice_entries_params
if invoice_entry_params[:id].nil?
invoice_entry = InvoiceEntry.new(invoice_entry_params)
errors << invoice_entry.errors.messages.values if not invoice_entry.valid?
else
invoice_entry = InvoiceEntry.find_by_id(invoice_entry_params[:id])
if invoice_entry.nil?
errors << "Couldn't find invoice entry with id = #{invoice_entry_params[:id]}"
else
invoice_entry.assign_attributes(invoice_entry_params)
errors << invoice_entry.errors.messages.values if not invoice_entry.valid?
end
end
invoice_entries << invoice_entry
end
# Invoice
@invoice.assign_attributes(date: params[:date])
errors << @invoice.errors.messages.values if not @invoice.valid?
if errors.empty?
# Save everything
@invoice.invoice_entries = invoice_entries
@invoice.save
head :no_content
else
render json: errors.flatten, status: :unprocessable_entity
end
end
要不僅更改關聯,還更改關聯對象的屬性,必須使用accepts_nested_attributes_for
:
class Invoice < ActiveRecord::Base
has_many :invoice_entries, :autosave => true, :dependent => :destroy
validates_presence_of :date
accepts_nested_attributes_for :invoice_entries, allow_destroy: true
end
關於如何使用nested_attributes
構建動態嵌套表單的信息,請參見第196集 。
附錄:
accepts_nested_attributes_for
在嵌套哈希中期望嵌套模型的屬性,即:
invoice_params={"date" => '2013-06-16',
"invoice_entries_attributes" => [
{"description" => "do A"},
{"description" => "do B"}]
}
invoice= Invoice.new(invoice_params)
invoice.save
save
將保存invoice
和兩個invoice_items
。
現在
invoice=Invoice.find(1)
invoice_params={
"invoice_entries_attributes" => [
{"description" => "do A"},
{"description" => "do C"}]
}
invoice.update_attributes(invoice_params)
刪除項目do B
並添加項目do C
form_fields
可用於創建形式,從而使這種嵌套的哈希值form_fields
。
有關詳細信息,請參見railscast。
嘗試使用accepts_nested_attributes_for
。 這將清理大量代碼! 這是一個例子:
class Invoice < ActiveRecord::Base
has_many :invoice_entries, :dependent => :destroy
validates_presence_of :date
attr_accessible :invoice_entries_attributes
accepts_nested_attributes_for :invoice_entries, :allow_destroy => true
end
然后可以在視圖中使用fields_for
(如果使用這些gem之一,則使用simple形式的simple_fields_for
和使用fields_for
semantic_fields_for
)。
<%= form_for @invoice do |invoice_form| %>
<%= invoice_form.fields_for :invoice_entries do |invoice_entry_form| %>
<%= invoice_entry_form.text_field :description %>
<%= invoice_entry_form.check_box :_destroy %>
<% end %>
<% end %>
您現在可以在控制器中重構為基礎知識:
# PATCH/PUT /api/invoices/5
def update
@invoice = Invoice.find(params[:id])
if @invoice.update_attributes(params[:invoice]) # This also saves all associated invoice entries, and destroy all that is marked for destruction.
head :no_content
else
render json: @invoice.errors.flatten, status: :unprocessable_entity
end
end
您可以在此處閱讀有關accepts_nested_attributes_for
更多信息: http : //api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
或者,您可以觀看有關嵌套模型的以下欄目: http ://railscasts.com/episodes/196-nested-model-form-revised
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.