跳至内容 跳至搜索

Active Record 自动保存关联

AutosaveAssociation 是一个模块,它负责在父级保存时自动保存关联的记录。除了保存之外,它还会销毁任何标记为要销毁的关联记录。(请参阅 mark_for_destructionmarked_for_destruction?)。

父级、其关联以及标记的关联的销毁的保存都在事务中进行。这永远不会使数据库处于不一致状态。

如果任何关联的验证失败,其错误消息将应用于父级。

请注意,这也意味着标记为要销毁的关联不会直接销毁。但是,它们仍将标记为要销毁。

请注意,autosave: false 与不声明 :autosave 不同。当 :autosave 选项不存在时,新关联记录将被保存,但更新的关联记录不会被保存。

验证

除非 :validatefalse,否则会验证子记录。

回调

带有 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。唯一的例外是如果使用了自定义验证上下文,在这种情况下,验证将始终在关联记录上触发。

方法
C
D
M
R

实例公共方法

changed_for_autosave?()

返回此记录是否已以任何方式更改(包括其任何嵌套的自动保存关联是否也已更改)

# File activerecord/lib/active_record/autosave_association.rb, line 271
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 265
def destroyed_by_association
  @destroyed_by_association
end

destroyed_by_association=(reflection)

记录正在销毁的关联,并在过程中销毁此记录。

# File activerecord/lib/active_record/autosave_association.rb, line 258
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 245
def mark_for_destruction
  @marked_for_destruction = true
end

marked_for_destruction?()

返回此记录是否将作为父保存事务的一部分而销毁。

仅当此关联模型的父上的:autosave选项已启用时才有用。

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

reload(options = nil)

像往常一样重新加载对象的属性,并清除 marked_for_destruction 标记。

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