Я хочу иметь модель, где мне нужно мягко удалить запись, а не показывать ее в поиске или любых других условиях во время поиска.
Я хочу сохранить модель без удаления записи. Как это сделать?
Я хочу иметь модель, где мне нужно мягко удалить запись, а не показывать ее в поиске или любых других условиях во время поиска.
Я хочу сохранить модель без удаления записи. Как это сделать?
Попробуйте этот камень: https://github.com/technoweenie/acts_as_paranoid - плагин ActiveRecord, позволяющий скрывать и восстанавливать записи, не удаляя их фактически
Просто используйте беспокойство в рельсах 4
Пример здесь
module SoftDeletable
extend ActiveSupport::Concern
included do
default_scope { where(is_deleted: false) }
scope :only_deleted, -> { unscope(where: :is_deleted).where(is_deleted: true) }
end
def delete
update_column :is_deleted, true if has_attribute? :is_deleted
end
def destroy;
callbacks_result = transaction do
run_callbacks(:destroy) do
delete
end
end
callbacks_result ? self : false
end
def self.included(klazz)
klazz.extend Callbacks
end
module Callbacks
def self.extended(klazz)
klazz.define_callbacks :restore
klazz.define_singleton_method("before_restore") do |*args, &block|
set_callback(:restore, :before, *args, &block)
end
klazz.define_singleton_method("around_restore") do |*args, &block|
set_callback(:restore, :around, *args, &block)
end
klazz.define_singleton_method("after_restore") do |*args, &block|
set_callback(:restore, :after, *args, &block)
end
end
end
def restore!(opts = {})
self.class.transaction do
run_callbacks(:restore) do
update_column :is_deleted, false
restore_associated_records if opts[:recursive]
end
end
self
end
alias :restore :restore!
def restore_associated_records
destroyed_associations = self.class.reflect_on_all_associations.select do |association|
association.options[:dependent] == :destroy
end
destroyed_associations.each do |association|
association_data = send(association.name)
unless association_data.nil?
if association_data.is_deleted?
if association.collection?
association_data.only_deleted.each { |record| record.restore(recursive: true) }
else
association_data.restore(recursive: true)
end
end
end
if association_data.nil? && association.macro.to_s == 'has_one'
association_class_name = association.options[:class_name].present? ? association.options[:class_name] : association.name.to_s.camelize
association_foreign_key = association.options[:foreign_key].present? ? association.options[:foreign_key] : "#{self.class.name.to_s.underscore}_id"
Object.const_get(association_class_name).only_deleted.where(association_foreign_key, self.id).first.try(:restore, recursive: true)
end
end
clear_association_cache if destroyed_associations.present?
end
end
Рельсы касаются добавления мягких удалений.
Очень простой и гибкий способ настройки/изменения
(Вы можете изменить столбец удаления на временную метку и изменить методы для вызова ActiveRecord touch).
Лучше всего, когда вы хотите контролировать код, не имеющий гемов для простых задач.
В ваших таблицах добавьте логический столбец is_deletable
class AddDeletedAtToUsers < ActiveRecord::Migration
def change
add_column :users, :is_deleted, :boolean
end
end
class User < ActiveRecord::Base
has_many :user_details, dependent: :destroy
include SoftDeletable
end
User.only_deleted
User.first.destroy
User.first.restore
User.first.restore(recursive: true)
Примечание. Сфокусируйтесь с помощью update_column или коснитесь, если вы решите использовать столбец отметки времени.
Если вы используете rails <= 3.x (в этом примере вместо логического поля также используется поле DateTime), есть некоторые различия:
module SoftDeletable
extend ActiveSupport::Concern
included do
default_scope { where(deleted_at: nil }
# In Rails <= 3.x to use only_deleted, do something like 'data = Model.unscoped.only_deleted'
scope :only_deleted, -> { unscoped.where(table_name+'.deleted_at IS NOT NULL') }
end
def delete
update_column :deleted_at, DateTime.now if has_attribute? :deleted_at
end
# ... ... ...
# ... OTHERS IMPLEMENTATIONS ...
# ... ... ...
def restore!(opts = {})
self.class.transaction do
run_callbacks(:restore) do
# Remove default_scope. "UPDATE ... WHERE (deleted_at IS NULL)"
self.class.send(:unscoped) do
update_column :deleted_at, nil
restore_associated_records if opts[:recursive]
end
end
end
self
end
alias :restore :restore!
def restore_associated_records
destroyed_associations = self.class.reflect_on_all_associations.select do |association|
association.options[:dependent] == :destroy
end
destroyed_associations.each do |association|
association_data = send(association.name)
unless association_data.nil?
if association_data.deleted_at?
if association.collection?
association_data.only_deleted.each { |record| record.restore(recursive: true) }
else
association_data.restore(recursive: true)
end
end
end
if association_data.nil? && association.macro.to_s == 'has_one'
association_class_name = association.options[:class_name].present? ? association.options[:class_name] : association.name.to_s.camelize
association_foreign_key = association.options[:foreign_key].present? ? association.options[:foreign_key] : "#{self.class.name.to_s.underscore}_id"
Object.const_get(association_class_name).only_deleted.where(association_foreign_key, self.id).first.try(:restore, recursive: true)
end
end
clear_association_cache if destroyed_associations.present?
end
end
В ваших таблицах добавьте столбец DateTime delete_at
class AddDeletedAtToUsers < ActiveRecord::Migration
def change
add_column :users, :deleted_at, :datetime
end
end
Просто добавьте логическое поле под названием deleted
или что-то в этом роде. Когда вы мягко удаляете запись, просто установите для этого поля значение true.
При выполнении поиска просто добавьте это как условие (или создайте область для него).
Функциональность default_scope
в ActiveRecord 3 делает это легко, но лично я предпочитаю широкий спектр стандартных решений, которые можно отбросить в проект. acts_as_archive
, в частности, наилучшим образом подходит для большинства моих проектов, поскольку он перемещает редкие записи удаленных записей в отдельную таблицу, позволяя базовой таблице оставаться маленькой и в ОЗУ сервера базы данных.
В зависимости от ваших потребностей вы также можете рассмотреть versioning вместо мягкого удаления.
Вы можете определить модуль, подобный этому
module ActiveRecordScope
def self.included(base)
base.scope :not_deleted, -> { base.where(deleted: false) }
base.send(:default_scope) { base.not_deleted }
base.scope :only_deleted, -> { base.unscope(where: :deleted).where(deleted: true) }
def delete
update deleted: true
end
def recover
update deleted: false
end
end
end
Затем в вашем классе вы можете написать что-то вроде:
class User < ActiveRecord::Base
include ActiveRecordScope
end
Итак, у вас есть как мягкое удаление, так и восстановление.
Вы вызываете user.delete
для мягкого удаления пользователя. Затем вы можете вызвать user.recover
, чтобы снова вернуть удаленный элемент обратно в false и восстановить его.
Посмотрите rails3_acts_as_paranoid.
Простой плагин, который скрывает записи вместо их удаления, будучи способные восстановить их.
...
Этот плагин был вдохновлен acts_as_paranoid и acts_as_active.
Применение:
class Paranoiac < ActiveRecord::Base
acts_as_paranoid
scope :pretty, where(:pretty => true)
end
Paranoiac.create(:pretty => true)
Paranoiac.pretty.count #=> 1
Paranoiac.only_deleted.count #=> 0
Paranoiac.pretty.only_deleted.count #=> 0
Paranoiac.first.destroy
Paranoiac.pretty.count #=> 0
Paranoiac.only_deleted.count #=> 1
Paranoiac.pretty.only_deleted.count #=> 1
Если вы используете Rails4, попробуйте этот камень: https://github.com/alfa-jpn/kakurenbo
Функция ассоциации Какуренбо лучше, чем другие драгоценные камни.
Я не буду использовать причину по умолчанию, если я хочу получить все записи, мне нужно игнорировать его с помощью "with_exclusive_scope", который, в свою очередь, беспорядочен. это может быть связано с добавлением "удаленного" логического поля, которое устанавливается, когда запись удаляется. Кроме того, было бы добавлено области для получения данных в соответствии с условием.
checkout Переопределение Rails default_scope и Rails: Почему защита с_exclusive_scope защищена? Любая хорошая практика по ее использованию?
Добавьте один столбец, указывающий статус в вашей таблице, и при удалении записей обновите значение статуса столбца до неактивного. и, извлекая записи, добавьте условие condition!= "inactive" в запросе.
Для Rails 4 не используйте act_as_paranoid (багги для Rails 4), используйте paranoia. Все, что вам нужно сделать, это добавить столбец timestamp deleted_at и включить act_as_paranoia в модель.
Оттуда просто вызовите destroy
на объект, а все остальные отношения ActiveRecord и большинство других методов (например: count) автоматически исключают записи soft_deleted.