Delete associated records when model is saved

ⅰ亾dé卋堺 提交于 2019-12-11 03:06:27

问题


I am running ActiveRecord 3.2.6. Given I have these model definitions:

My Invoice model

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

My Item model

class Item < ActiveRecord::Base
  belongs_to :invoice

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

My goal is to save the invoice items directly in the model. This works without problems, when the Invoice is freshly created. One call to Invoice#save! and everything is saved.

> 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 

However, when I try to update the items in an Invoice that already exists, it deletes the old items before I save the new items.

# 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

How can I tell ActiveRecord to delete the old items only when Invoice#save! is called? Or is this a bug in ActiveRecord?

EDIT: My question - clarified

I do not want the DELETE queries to run when the items are assigned (i.items = ...), but when the invoice containing the items is saved (invoice.save!). It should mark the old items for deletions and the new items for insertation and then execute the query on invoice.save!. Is this possible with ActiveRecord?

EDIT 2: Further clarification

As some do not get the question right, some further clarifications. I must admit, this is pretty complex. So here is the difference between what actually happens and what I want to happen.

What I want

This does not happen. I want it to happen. This is completely fictitious. Compare it to the listing above to see the difference.

# (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

What actually hapens

The DELETE query runs on at point (2). But it should run at point (3). (Compare to above listing).


回答1:


It will delete the old items because you configured association as autosave

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

Remove autosave and try again, it will work.




回答2:


Since you want append action in assignment, I believe this should work:

  def items=(ary)
    super(ary.map{|item| item.is_a?(Hash) ? items.build(item) : item} + self.items)
  end


来源:https://stackoverflow.com/questions/11353582/delete-associated-records-when-model-is-saved

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!