delete_all vs destroy_all?

193

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

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all
u.sources.destroy_all
u.user_stats.destroy_all
u.delete

Это работает и удаляет все ссылки пользователя из всех таблиц, но я слышал, что это destroy_allбыло очень тяжело, поэтому я попытался delete_all. Он только удаляет пользователя из его собственной пользовательской таблицы, а idвсе остальные таблицы обнуляются, но оставляют записи в них нетронутыми. Может кто-нибудь поделиться тем, что правильный процесс для выполнения такой задачи?

Я вижу, что destroy_allвызывает destroyфункцию на всех связанных объектах, но я просто хочу подтвердить правильный подход.

glogic
источник

Ответы:

244

Ты прав. Если вы хотите удалить пользователя и все связанные объекты -> destroy_all Однако, если вы просто хотите удалить пользователя, не подавляя все связанные объекты ->delete_all

Согласно этому посту: Rails: зависимый =>: уничтожить VS: зависимый =>: delete_all

  • destroy/ destroy_all: Связанные объекты уничтожаются вместе с этим объектом, вызывая их метод уничтожения
  • delete/ delete_all: Все связанные объекты уничтожаются немедленно, без вызова их метода: destroy
Сандро Мунда
источник
80
Также следует отметить, что 1) обратные вызовы не вызываются при использовании delete_all, и 2) destroy_allсоздает все записи и уничтожает их по одной за раз, поэтому при очень большом наборе данных это может быть мучительно медленным.
Дилан Марков
Предположим, я запускаю метод before_destroy в модели - если я использую delete_all, то этот метод не будет работать? во-вторых, если я использую метод before_delete в моей модели, будет ли он выполняться при запуске delete или delete_all в консоли rails?
BKSpurgeon
23

delete_all - это отдельный оператор SQL DELETE и ничего более. destroy_all вызывает destroy () для всех совпадающих результатов: условий (если они у вас есть), которые могут быть как минимум NUM_OF_RESULTS SQL-операторами.

Если вам нужно сделать что-то радикальное, например destroy_all () для большого набора данных, я бы, вероятно, не сделал бы это из приложения и не обработал бы это вручную с осторожностью. Если набор данных достаточно мал, вам не будет так больно.

Райан Хер
источник
16

Чтобы избежать того факта, что destroy_allсоздаются все записи и уничтожаются по одной за раз, вы можете использовать их непосредственно из класса модели.

Так что вместо:

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all

Ты можешь сделать :

u = User.find_by_name('JohnBoy')
UsageIndex.destroy_all "user_id = #{u.id}"

В результате один запрос уничтожает все связанные записи

симон-оливье
источник
1
Будет ли он вызывать обратные вызовы уничтожения для связанных записей или UsageIndex.destroy_allэквивалентен UsageIntex.delete_all?
Магне
UsageIndex.destroy_allбольше не доступен, так как рельсы 3
fabriciofreitag
1

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

Этот драгоценный камень добавляет новую опцию для ассоциаций ActiveRecord:

зависимый:: delete_recursively

Когда вы уничтожаете запись, все записи, связанные с этим параметром, будут рекурсивно удаляться (то есть в разных моделях) без создания какой-либо из них.

Обратите внимание, что, как и в случае с зависимым:: удалить или зависимым:: удалить, все эти новые параметры не вызывают обратные вызовы вокруг / до / после_дестрой зависимых записей.

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

Janosch
источник
Это фантастика! Интересно, почему не так много людей смотрели / снимали / разветвляли его на github ... это все еще работает хорошо?
Магне
@Magne Спасибо! Это должно работать. Тесты работают на Ruby 2.4.1 и Rails 5.1.1. До сих пор я использовал его только в личных целях, а не в основных производственных приложениях, следовательно, в основной версии «0», но я никогда не замечал никаких проблем. Это также довольно просто, так что должно быть хорошо.
Янош
Прохладно. :) Я запускаю проект на Ruby 2.3.1 и 'rails', '~> 4.1.14', и, к сожалению, вынужден полагаться на activerecord (~> 4.1.0) из-за других драгоценных камней. Я вижу, что delete_recursively разрешен до 0.9.0. Существует ли более старая версия, которая будет работать с activerecord 4.1? Я не смог найти ничего на вкладке релизов на github.
Магне
1
@Magne, я нашел это на самом деле работает для activerecord, начиная с 4.1.14, и выпустил версию gem 1.0.0 со слабой зависимостью. Имейте в виду, что ветка Rails 4.1 больше не получает обновления безопасности.
Янош