delete_all vs destroy_all?

192

Estou procurando a melhor abordagem para excluir registros de uma tabela. Por exemplo, eu tenho um usuário cujo ID do usuário está em várias tabelas. Quero excluir este usuário e todos os registros que tenham seu ID em todas as tabelas.

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

Isso funciona e remove todas as referências do usuário de todas as tabelas, mas ouvi dizer que o destroy_allprocesso era muito pesado, então tentei delete_all. Ele remove apenas o usuário de sua própria tabela de usuários e as idde todas as outras tabelas são tornadas nulas, mas deixa os registros intactos nelas. Alguém pode compartilhar qual é o processo correto para executar uma tarefa como esta?

Vejo que destroy_allchama a destroyfunção em todos os objetos associados, mas só quero confirmar a abordagem correta.

glogic
fonte

Respostas:

243

Você está certo. Se você deseja excluir o Usuário e todos os objetos associados -> destroy_all No entanto, se você deseja excluir o Usuário sem suprimir todos os objetos associados ->delete_all

De acordo com este post: Rails: dependente =>: destroy VS: dependente =>: delete_all

  • destroy/ destroy_all: Os objetos associados são destruídos ao lado desse objeto chamando seu método de destruição
  • delete/ delete_all: Todos os objetos associados são destruídos imediatamente sem chamar o método: destroy
Sandro Munda
fonte
80
Também deve ser observado que 1) os retornos de chamada não são chamados durante o uso delete_alle 2) destroy_allinstancia todos os registros e os destrói um de cada vez; portanto, com um conjunto de dados muito grande, isso pode ser dolorosamente lento.
Dylan Markow
suponha que eu esteja executando um método before_destroy no modelo - se eu usar delete_all, esse método não será executado? segundo, se eu empregar um método before_delete no meu modelo, isso será executado quando eu executar delete ou delete_all no console do rails?
BKSpurgeon
23

delete_all é uma única instrução SQL DELETE e nada mais. destroy_all chama destroy () em todos os resultados correspondentes de: conditions (se você tiver um) que poderia ser pelo menos NUM_OF_RESULTS instruções SQL.

Se você tiver que fazer algo drástico, como destroy_all () em um grande conjunto de dados, provavelmente não faria isso no aplicativo e manipularia manualmente com cuidado. Se o conjunto de dados for pequeno o suficiente, você não machucaria tanto.

Ryan Her
fonte
16

Para evitar o fato de que destroy_allinstancia todos os registros e os destrói um de cada vez, você pode usá-lo diretamente da classe de modelo.

Então, em vez de:

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

Você pode fazer :

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

O resultado é uma consulta para destruir todos os registros associados

simon-olivier
fonte
1
Ele chamará os retornos de destruição em registros associados ou é UsageIndex.destroy_allequivalente a UsageIntex.delete_all?
Magne12
UsageIndex.destroy_allnão está mais disponível desde os trilhos 3
fabriciofreitag
1

Fiz uma pequena jóia que pode aliviar a necessidade de excluir manualmente os registros associados em algumas circunstâncias.

Esta gema adiciona uma nova opção para associações do ActiveRecord:

dependente:: delete_recursively

Quando você destrói um registro, todos os registros associados a essa opção serão excluídos recursivamente (ou seja, entre modelos), sem instanciar nenhum deles.

Observe que, assim como dependente:: delete ou dependente:: delete_all, essa nova opção não aciona os retornos de chamada around / before / after_destroy dos registros dependentes.

No entanto, é possível ter dependentes:: destruir associações em qualquer lugar dentro de uma cadeia de modelos que, de outra forma, estão associados a dependentes:: delete_recursively. A opção: destroy funcionará normalmente em qualquer lugar acima ou abaixo da linha, instanciando e destruindo todos os registros relevantes e, assim, também acionando seus retornos de chamada.

Janosch
fonte
Isto é fantástico! Eu me pergunto por que não mais pessoas assistiram / estrelaram / bifurcaram no github .. ainda está funcionando bem?
Magne12
@Magne Thanks! Deveria estar funcionando. Os testes são executados no Ruby 2.4.1 e no Rails 5.1.1. Até agora, eu o usei apenas em particular e não nos principais aplicativos de produção, daí a versão principal "0", mas nunca notei nenhum problema. Também é bastante simples, por isso deve ficar bem.
Janosch
Legal. :) Estou executando um projeto no Ruby 2.3.1 e 'rails', '~> 4.1.14' e, infelizmente, sou forçado a confiar no registro do ativo (~> 4.1.0) devido a outras gemas. Vejo que delete_recursively foi resolvido para 0.9.0. Existe uma versão mais antiga que funcionaria com o activerecord 4.1? Não encontrei nenhum na guia releases no github.
Magne15 de
1
@Magne Achei que ele realmente funciona para o registro ativo até 4.1.14 e lançou a versão 1.0.0 da gem com uma dependência descontraída. Lembre-se de que a ramificação 4.1 do Rails não recebe mais atualizações de segurança.
Janosch