модуль 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 из-за циклических валидаций ассоциаций. Единственное исключение — если используется пользовательский контекст валидации, в этом случае валидация всегда будет выполняться для связанных записей.
Публичные методы экземпляров
# 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 # 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
Возвращает значение true, если эта запись была изменена каким-либо образом (включая случай, когда любая из её вложенных ассоциаций autosave также была изменена).
# File activerecord/lib/active_record/autosave_association.rb, line 269 def destroyed_by_association @destroyed_by_association end
Возвращает ассоциацию родительской записи, которая уничтожается.
Используется для предотвращения ненужного обновления счётчика.
# File activerecord/lib/active_record/autosave_association.rb, line 262 def destroyed_by_association=(reflection) @destroyed_by_association = reflection end
Записывает ассоциацию, которая уничтожается, и уничтожает эту запись в процессе.
# File activerecord/lib/active_record/autosave_association.rb, line 249 def mark_for_destruction @marked_for_destruction = true end
Помечает эту запись на удаление в рамках транзакции сохранения родительской записи. Это не фактически уничтожает запись немедленно, а дочерняя запись будет удалена при вызове parent.save.
Полезно только если для этой связанной модели включена опция :autosave родительской записи.
# File activerecord/lib/active_record/autosave_association.rb, line 256 def marked_for_destruction? @marked_for_destruction end
Возвращает значение true, если эта запись будет удалена в рамках транзакции сохранения родительской записи.
Полезно только если для этой связанной модели включена опция :autosave родительской записи.
# 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.
# 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.