модуль ActiveRecord::AutosaveAssociation
Ассоциация Active Record с автоматическим сохранением
AutosaveAssociation — это модуль, который отвечает за автоматическое сохранение связанных записей при сохранении родительской записи. Помимо сохранения, он также удаляет любые связанные записи, помеченные на удаление. (См. mark_for_destruction и marked_for_destruction?).
Сохранение родителя, его ассоциаций и удаление помеченных ассоциаций происходит внутри транзакции. Это никогда не должно оставлять базу данных в несогласованном состоянии.
Если проверки для какой-либо из ассоциаций не пройдут, их сообщения об ошибках будут применены к родителю.
Обратите внимание, что это также означает, что ассоциации, помеченные на удаление, не будут удалены непосредственно. Однако они по-прежнему будут помечены на удаление.
Обратите внимание, что autosave: false не эквивалентно отсутствию объявления :autosave. Если опция :autosave отсутствует, то новые записи ассоциаций сохраняются, но обновленные записи ассоциаций не сохраняются.
Проверка
Записи дочерних элементов проверяются, если :validate не false.
Callbacks
Ассоциация с опцией 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 271 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 265 def destroyed_by_association @destroyed_by_association end
Возвращает ассоциацию родительского элемента, который удаляется.
Используется для предотвращения ненужного обновления счётчика кэша.
# File activerecord/lib/active_record/autosave_association.rb, line 258 def destroyed_by_association=(reflection) @destroyed_by_association = reflection end
Записывает ассоциацию, которая уничтожается, и уничтожает запись в процессе.
# File activerecord/lib/active_record/autosave_association.rb, line 245 def mark_for_destruction @marked_for_destruction = true end
Помечает запись на удаление в рамках транзакции сохранения родителя. Это не фактически уничтожает запись мгновенно, а дочерняя запись будет уничтожена, когда будет вызвано parent.save.
Полезно только в том случае, если опция :autosave для родительского элемента включена для этой связанной модели.
# File activerecord/lib/active_record/autosave_association.rb, line 252 def marked_for_destruction? @marked_for_destruction end
Возвращает значение true, если запись будет удалена в рамках транзакции сохранения родительского элемента.
Полезно только в том случае, если опция :autosave для родительского элемента включена для этой связанной модели.
# 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.