Rails“has_many”关联:“collection =”不能按预期工作

时间:2013-06-16 13:14:18

标签: ruby-on-rails ruby-on-rails-3.2 rails-activerecord

发票有很多发票条目:

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在id中看到现有的new_invoice_entries时,它完全忽略了它。真的吗?如果是,这背后的理由是什么?

我的完整代码如下。你会如何解决这个问题? (我使用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

2 个答案:

答案 0 :(得分:0)

要更改关联对象以及相关对象的属性,您必须使用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构建动态嵌套表单,有railscast episode 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可用于创建导致这种嵌套哈希值极为明显的表单。
有关详细信息,请参阅railscast。

答案 1 :(得分:0)

尝试使用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_forsimple_fields_for使用简单形式,semantic_fields_for使用formtastic(如果您使用其中一种宝石)。

<%= 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_forhttp://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

或者您可以观看有关嵌套模型的此railscast:http://railscasts.com/episodes/196-nested-model-form-revised