我正在运行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
我的目标是将发票项目直接保存在模型中。当新创建发票时,这没有问题。一次致电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
)时运行i.items = ...
个查询,但保存包含项目的发票时invoice.save!
)。它应标记旧项目以进行删除,并将新项目标记为插入,然后在invoice.save!
上执行查询。这可能与ActiveRecord有关吗?
由于有些人不能正确回答问题,因此需要进一步澄清。我必须承认,这非常复杂。所以这是实际发生的事情和我想要发生的事情之间的区别。
此不会发生。我希望它发生。这完全是虚构的。将其与上面的列表进行比较,看看差异。
# (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)
点运行。 (与上面的清单相比)。
答案 0 :(得分:0)
它会删除旧项目,因为您将关联配置为自动保存
has_many :items, :autosave => true, :dependent => :delete_all
删除自动保存并重试,它会起作用。
答案 1 :(得分:0)
由于您希望在作业中添加动作,我相信这应该有效:
def items=(ary)
super(ary.map{|item| item.is_a?(Hash) ? items.build(item) : item} + self.items)
end
答案 2 :(得分:0)
您可以使用autosave: true
选项配置关联,并使用mark_for_destruction在save
上指定要销毁的每个物品。
考虑第二次修改,您可以执行以下操作:
# (1) Load invoice ID 1, with two items: ID 1 and ID 2.
> i = Invoice.find(1)
# (2) Mark the old items to be destroyed on save. They are still in memory and in the database for now.
> i.items.each(&:mark_for_destruction)
# (3) Assign new items. New stuff exists in memory, not in database.
> i.items.build([{amount: 10, description: "Less buggy objective relational mappers", value: 1337.00}])
# (4) Delete the items which has been mark for destruction. 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