Active Record 自动保存关联
AutosaveAssociation
是一个模块,负责在保存父记录时自动保存关联记录。除了保存,它还会销毁任何被标记为销毁的关联记录。(参见 mark_for_destruction
和 marked_for_destruction?
)。
保存父记录、其关联和销毁标记的关联,所有操作都在一个事务中完成。这应该永远不会导致数据库处于不一致状态。
如果任何关联的验证失败,它们的错误消息将应用于父记录。
请注意,这也意味着被标记为销毁的关联不会直接被销毁。但是,它们仍然会被标记为销毁。
请注意,autosave: false
与不声明 :autosave
不同。当 :autosave
选项不存在时,会保存新的关联记录,但不会保存更新的关联记录。
验证
除非 :validate
为 false
,否则子记录会被验证。
回调
具有 autosave 选项的关联在您的模型上定义了几个回调(around_save、before_save、after_create、after_update)。请注意,回调按其在模型中定义的顺序执行。您应该避免在 autosave 回调执行之前修改关联内容。将您的回调放在关联之后通常是一个好的做法。
一对一示例
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
注意事项
请注意,如果关联记录本身发生了更改,则只有对已持久化的关联记录才会触发 autosave。这是为了防止由循环关联验证引起的 SystemStackError
。唯一例外是使用自定义验证上下文的情况,在这种情况下,验证始终会在关联记录上触发。
- A
- C
- D
- M
- R
- V
实例公共方法
autosaving_belongs_to_for?(association) 链接
来源:显示 | 在 GitHub 上
# 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?() 链接
返回此记录是否已以任何方式更改(包括其嵌套的 autosave 关联是否也已更改)。
来源:显示 | 在 GitHub 上
# 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() 链接
返回正在销毁的父记录的关联。
用于避免不必要地更新计数器缓存。
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/autosave_association.rb, line 269 def destroyed_by_association @destroyed_by_association end
destroyed_by_association=(reflection) 链接
记录正在销毁的关联,并在过程中销毁此记录。
来源:显示 | 在 GitHub 上
# 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
选项时才有效。
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/autosave_association.rb, line 249 def mark_for_destruction @marked_for_destruction = true end
marked_for_destruction?() 链接
返回此记录是否将作为父记录保存事务的一部分进行销毁。
只有当父记录上针对此关联模型启用了 :autosave
选项时才有效。
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/autosave_association.rb, line 256 def marked_for_destruction? @marked_for_destruction end
reload(options = nil) 链接
像往常一样重新加载对象的属性,并清除 marked_for_destruction
标志。
来源:显示 | 在 GitHub 上
# 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) 链接
来源:显示 | 在 GitHub 上
# 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