Spec-Zone.ru › Ruby on Rails 7.1

модуль ActiveRecord::AutosaveAssociation

Связывание Active Record с автосохранением

AutosaveAssociation — это модуль, который отвечает за автоматическое сохранение связанных записей при сохранении их родительской записи. Кроме сохранения, он также уничтожает любые связанные записи, помеченные на удаление. (См. mark_for_destruction и marked_for_destruction?).

Сохранение родительской записи, её ассоциаций и уничтожение помеченных ассоциаций происходят внутри транзакции. Это должно гарантировать, что состояние базы данных никогда не будет несогласованным.

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

Обратите внимание, что это также означает, что ассоциации, помеченные на удаление, не будут удалены непосредственно. Однако они всё равно будут помечены на удаление.

Обратите внимание, что autosave: false не то же самое, что и отсутствие объявления :autosave. Когда опция :autosave отсутствует, новые записи ассоциации сохраняются, но обновлённые записи ассоциации не сохраняются.

Проверка

Записи дочерних элементов проверяются, если :validate не false.

Обработчики событий

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

Открытые методы экземпляра

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

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

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

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

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

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

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

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

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

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

Вызывает метод суперкласса

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

Spec-Zone.ru

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