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)
。(上記のリストと比較してください)。