4

ActiveRecord 3.2.6 を実行しています。これらのモデル定義があるとします。

私の請求書モデル

class Invoice < ActiveRecord::Base
  has_many :items, :autosave => true, :dependent => :delete_all
  attr_accessible :recipient_email

  # This is just a simple wrapper with allows me to build multiple
  # items at once and to specify them as a Hash instead of Item.new.
  def items=(ary)
    super ary.map{|item| item.is_a?(Hash) ? items.build(item) : item}
  end
end

マイアイテムモデル

class Item < ActiveRecord::Base
  belongs_to :invoice

  attr_accessible :amount, :description, :invoice_id, :value
end

私の目標は、請求書の項目をモデルに直接保存することです。請求書が新しく作成されたとき、これは問題なく機能します。1 回の呼び出しInvoice#save!ですべてが保存されます。

> i = Invoice.new(:recipient_email => "foobar@example.org")
> i.items = [{amount: 10, description: "Bottles of Milk", value: 0.40},
  {amount: 1, description: "Shipping fee to Antarctica", value: 217.38}]
> i.save!
  SQL (23.5ms)  INSERT INTO "invoices" [...]
  SQL (0.3ms)  INSERT INTO "items" [...]
  SQL (0.2ms)  INSERT INTO "items" [...]
 => true 

ただし、既存のアイテムを更新しようとするとInvoice、新しいアイテムを保存する前に古いアイテムが削除されます。

# Load invoice ID 1, with two items: ID 1 and ID 2.
> i = Invoice.find(1)

# It deletes the old items here
> i.items = [{amount: 10, description: "Less buggy objective relational mappers", value: 1337.00}]
  SQL (0.8ms)  DELETE FROM items WHERE id IN (1, 2)

# But it should delete the new items here, before inserting the new items,
# wrapping everything in a transaction.
> i.save!
  SQL (1.0ms)  INSERT INTO "items" [...]
   (192.6ms)  commit transaction

Invoice#save!が呼び出されたときにのみ古いアイテムを削除するように ActiveRecord に指示するにはどうすればよいですか? それとも、これは ActiveRecord のバグですか?

編集:私の質問 - 明確化

アイテムが割り当てられたとき ( ) ではなく、アイテムを含む請求書が保存されたとき ( )DELETEにクエリを実行したくありません。古いアイテムを削除用にマークし、新しいアイテムを挿入用にマークしてから、 でクエリを実行する必要があります。これは ActiveRecord で可能ですか?i.items = ...invoice.save!invoice.save!

EDIT 2:さらなる明確化

質問を正しく理解していない人もいるので、さらに説明します。認めざるを得ませんが、これはかなり複雑です。ここに、実際に起こることと私が望んでいることの違いがあります。

私が欲しいもの

これは起こりません。私はそれが起こることを望んでいます。これは完全に架空のものです。上記のリストと比較して、違いを確認してください。

# (1) Load invoice ID 1, with two items: ID 1 and ID 2.
> i = Invoice.find(1)

# (2) Assign new items, delete the old ones. New stuff exists in memory, not in database
> i.items = [{amount: 10, description: "Less buggy objective relational mappers", value: 1337.00}]

# (3) Save everything to database. Run queries.
> i.save!
   (0.0ms) begin transactions
  SQL (0.8ms)  DELETE FROM items WHERE id IN (1, 2)
  SQL (1.0ms)  INSERT INTO "items" [...]
   (192.6ms)  commit transaction

実際に何が起こるか

DELETEクエリはポイント で実行されます(2)。しかし、ポイントで実行する必要があります(3)。(上記のリストと比較してください)。

4

3 に答える 3

0

関連付けを自動保存として構成したため、古いアイテムが削除されます

has_many :items, :autosave => true, :dependent => :delete_all

自動保存を削除して再試行してください。機能します。

于 2012-07-06T16:35:21.343 に答える
0

割り当てに追加アクションが必要なので、これは機能するはずです。

  def items=(ary)
    super(ary.map{|item| item.is_a?(Hash) ? items.build(item) : item} + self.items)
  end
于 2012-07-07T12:45:14.650 に答える