Spec-Zone.ru › Ruby on Rails 8.1

модуль ActiveRecord::AutosaveAssociation

Автоматическое сохранение ассоциаций 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, вызванные циклическими проверками ассоциаций. Исключение составляет использование пользовательского контекста проверки: в этом случае проверки связанных записей выполняются всегда.

Публичные методы экземпляра

autosaving_belongs_to_for? (association) Показать исходный код
# 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

Возвращает значение, указывающее, изменялась ли эта запись каким-либо образом (в том числе изменились ли какие-либо её вложенные ассоциации autosave).

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 () Показать исходный код
# File activerecord/lib/active_record/autosave_association.rb, line 249
def mark_for_destruction
  @marked_for_destruction = true
end

Помечает эту запись для удаления в рамках транзакции сохранения родительской записи. Запись не удаляется немедленно: дочерняя запись будет удалена при вызове parent.save.

Имеет смысл, только если для этой связанной модели включён параметр :autosave родительской записи.

marked_for_destruction? () Показать исходный код
# File activerecord/lib/active_record/autosave_association.rb, line 256
def marked_for_destruction?
  @marked_for_destruction
end

Возвращает значение, указывающее, будет ли эта запись удалена в рамках транзакции сохранения родительской записи.

Имеет смысл, только если для этой связанной модели включён параметр :autosave родительской записи.

reload (options = nil) Показать исходный код
# File activerecord/lib/active_record/autosave_association.rb, line 238
def reload(options = nil)
  @marked_for_destruction = false
  @destroyed_by_association = nil
  super
end

Как обычно, перезагружает атрибуты объекта и сбрасывает флаг marked_for_destruction.

Вызывает метод суперкласса
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

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API