跳至內容 跳至搜尋

Active Record 自動儲存關聯

AutosaveAssociation 是當其母物件儲存時,自動儲存相關記錄的模組。除了儲存之外,它還會銷毀任何已標記為要銷毀的關聯記錄。(見 mark_for_destructionmarked_for_destruction?)。

儲存母物件、其關聯物件,以及銷毀已標記關聯物件,都發生在交易內。這不應讓資料庫處於不一致的狀態。

如果任何關聯物件驗證失敗,其錯誤訊息會套用給母物件。

請注意,這也表示已標記為要銷毀的關聯是不會直接被銷毀的。但它們仍會被標記為要銷毀。

請注意,autosave: false 並不與不宣告 :autosave 相同。當沒有 :autosave 選項時,新的關聯記錄會被儲存,但更新的關聯記錄將不會儲存。

驗證

子記錄會被驗證,除非 :validatefalse

回呼程式

具備自動儲存選項的關聯物件會在您的模型中定義數個回呼程式(around_save、before_save、after_create、after_update)。請注意,回呼程式會依照在模型中定義的順序執行。您應避免在自動儲存回呼程式執行之前修改關聯物件內容。通常在關聯物件之後放置回呼程式會是不錯的作法。

一對一範例

class Post < ActiveRecord::Base
  has_one :author, autosave: true
end

現在能自動原子地儲存對母物件及相關模型變更。

post = Post.find(1)
post.title       # => "The current global position of migrating ducks"
post.author.name # => "alloy"

post.title = "On the migration of ducks"
post.author.name = "Eloy Duran"

post.save
post.reload
post.title       # => "On the migration of ducks"
post.author.name # => "Eloy Duran"

在母物件儲存動作中銷毀一個相關模型,會如標記為要銷毀般地簡單

post.author.mark_for_destruction
post.author.marked_for_destruction? # => true

請注意,此模型從資料庫中移除

id = post.author.id
Author.find_by(id: id).nil? # => false

post.save
post.reload.author # => nil

現在從資料庫中移除

Author.find_by(id: id).nil? # => true

一對多範例

當沒有宣告 :autosave 時,在母物件儲存時會儲存新的子物件

class Post < ActiveRecord::Base
  has_many :comments # :autosave option is not declared
end

post = Post.new(title: 'ruby rocks')
post.comments.build(body: 'hello world')
post.save # => saves both post and comment

post = Post.create(title: 'ruby rocks')
post.comments.build(body: 'hello world')
post.save # => saves both post and comment

post = Post.create(title: 'ruby rocks')
comment = post.comments.create(body: 'hello world')
comment.body = 'hi everyone'
post.save # => saves post, but not comment

:autosave 為 true 時,會儲存所有子物件,不論它們是否為新記錄

class Post < ActiveRecord::Base
  has_many :comments, autosave: true
end

post = Post.create(title: 'ruby rocks')
comment = post.comments.create(body: 'hello world')
comment.body = 'hi everyone'
post.comments.build(body: "good morning.")
post.save # => saves post and both comments.

在母物件儲存動作中銷毀其中一個相關模型,會如標記為要銷毀般地簡單

post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
post.comments[1].mark_for_destruction
post.comments[1].marked_for_destruction? # => true
post.comments.length # => 2

請注意,此模型從資料庫中移除

id = post.comments.last.id
Comment.find_by(id: id).nil? # => false

post.save
post.reload.comments.length # => 1

現在從資料庫中移除

Comment.find_by(id: id).nil? # => true

注意事項

請注意,自動儲存只會觸發已儲存的關聯物件記錄(前提是記錄本身已變更)。這可防止由循環關聯驗證所導致的 SystemStackError。但有一個例外,即使用了自訂的驗證內容,此時驗證會持續在相關記錄上觸發。

方法
A
C
D
M
R
V

執行個體的公開方法

autosaving_belongs_to_for?(關聯)

# File activerecord/lib/active_record/autosave_association.rb, line 284
def autosaving_belongs_to_for?(association)
  @autosaving_belongs_to_for ||= {}
  @autosaving_belongs_to_for[association]
end

changed_for_autosave?()

傳回此記錄是否已任何方式變更 (包括任何巢狀自動儲存關聯也已變更)

# File activerecord/lib/active_record/autosave_association.rb, line 275
def changed_for_autosave?
  new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
end

destroyed_by_association()

傳回要毀損的父關聯。

用於避免不必要地更新反向快取。

# File activerecord/lib/active_record/autosave_association.rb, line 269
def destroyed_by_association
  @destroyed_by_association
end

destroyed_by_association=(reflection)

記錄要毀損且毀損這個記錄的關聯。

# File activerecord/lib/active_record/autosave_association.rb, line 262
def destroyed_by_association=(reflection)
  @destroyed_by_association = reflection
end

mark_for_destruction()

標記這個記錄為在父儲存交易中被毀損的一部分。這不會立即毀損這個記錄,而是會在呼叫 parent.save 時毀損子記錄。

只有在這個關聯模型已啟用父的 :autosave 選項時才有用。

# File activerecord/lib/active_record/autosave_association.rb, line 249
def mark_for_destruction
  @marked_for_destruction = true
end

marked_for_destruction?()

傳回這個記錄是否會在父儲存交易中毀損。

只有在這個關聯模型已啟用父的 :autosave 選項時才有用。

# File activerecord/lib/active_record/autosave_association.rb, line 256
def marked_for_destruction?
  @marked_for_destruction
end

reload(options = nil)

重新載入物件的屬性並清除 marked_for_destruction 旗標。

# File activerecord/lib/active_record/autosave_association.rb, line 238
def reload(options = nil)
  @marked_for_destruction = false
  @destroyed_by_association = nil
  super
end

validating_belongs_to_for?(association)

# File activerecord/lib/active_record/autosave_association.rb, line 279
def validating_belongs_to_for?(association)
  @validating_belongs_to_for ||= {}
  @validating_belongs_to_for[association]
end