Posso configurar a exclusão do Cascade no Rails?

90

Sei que isso provavelmente está em algum lugar da Internet, mas não consigo encontrar a resposta aqui no Stackoverflow, então pensei em aumentar um pouco a base de conhecimento aqui.

Eu sou um novato em Ruby e Rails, mas minha empresa está investindo muito nisso, então estou tentando conhecê-la com um pouco mais de detalhes.

Tem sido difícil para mim mudar minha mentalidade para projetar um aplicativo a partir do "modelo" em vez do banco de dados, então estou tentando descobrir como faria todo o trabalho de design que fiz classicamente no banco de dados no Em vez disso, modelo Rails.

Portanto, a tarefa mais recente que me dei é descobrir como configurar um modelo de banco de dados Rails para fazer exclusões em cascata? Existe uma maneira fácil de fazer isso? Ou eu teria que entrar no MySql e configurar isso?

matt_dev
fonte

Respostas:

106

você também pode definir a opção: dependente para: delete_all. : delete_all emitirá uma única instrução SQL para excluir todos os registros filhos. por causa disso, usar: delete_all pode fornecer a você um melhor desempenho.

has_many :memberships, dependent: :delete_all
Mike Breen
fonte
8
Sua explicação é confusa. Uma única instrução SQL será usada, mas o método destroy não será chamado para cada linha filho. Você tem que usar destroy_all para isso.
John Topley
@John - espero que as edições esclareçam a confusão. Obrigado por apontar isso.
Mike Breen
27
Certifique-se de entender a diferença entre usar :delete_alle :destroypara isso. Ambos farão com que as associações filho (1 nível para deletar [carece de fontes] e npara destruir (se seus filhos tiverem destrói dependentes)) sejam removidas do banco de dados, mas :destroyirão instanciar cada objeto filho e executar qualquer retorno de chamada primeiro, enquanto :delete_allirá executar diretamente um Instrução SQL DELETE no banco de dados. :destroyé mais lento por causa disso, mas permite que você tenha retornos de chamada quando um registro é destruído. Contornando Rails em uma extremidade e potencial instanciação n ^ x na outra.
jstim
2
Eu sugiro também configurar as chaves estrangeiras do banco de dados. Dessa forma, os registros são excluídos com uma operação. Veja abaixo a resposta que postei.
Hendrik
66

Sim, você pode, se estiver usando um relacionamento como has_many, basta fazer isso

has_many :memberships, dependent: :destroy
Danmayer
fonte
Dan, Acho que minha próxima pergunta é se eu executar um comando db migrate, isso realmente configurará isso no banco de dados? Ou a cascata é totalmente controlada por trilhos?
matt_dev 01 de
Sim, é tratado por trilhos. (No entanto, certifique-se de que realmente sempre precisa excluir todas as linhas relacionadas.)
Stein G. Strindhaug
@Matt - a linha has_many deve estar em sua classe de modelo, a migração não adicionará isso para você.
Gareth
Eu prefiro essa solução porque ela também funciona se o modelo dependente tiver outra relação
has_many
27

Ao contrário da resposta fornecida, eu sugiro também fazer isso no nível do banco de dados. No caso de você ter processos diferentes ou um ambiente multi-threaded, pode acontecer que os registros não sejam apagados corretamente. Além disso, a chave estrangeira do banco de dados torna as coisas muito mais rápidas ao excluir muitos dados.

Como na resposta sugerida, faça o seguinte:

has_many :memberships, dependent: :delete_all

No entanto, também certifique-se de configurar um foreign_keyem uma migração. Dessa forma, o banco de dados se encarrega de excluir os registros automaticamente para você.

Para anular os valores quando uma associação é excluída, supondo que você tenha um modelo de usuário:

add_foreign_key :users, :memberships, on_delete: :nullify

Você também pode excluir todos os modelos sempre que uma associação for excluída

add_foreign_key :users, :memberships, on_delete: :cascade
Hendrik
fonte
Portanto, posso usar "has_many: memberships, Dependent:: delete_all" e "add_foreign_key: users,: Memberships, on_delete:: cascade"? Vai funcionar bem?
Rubycon
2
Você nem precisará configurar o delete_allno modelo. A chave estrangeira cuidará de deletar tudo apropriadamente para você no nível do banco de dados.
Hendrik
3
Estou curioso para saber o que acontece quando você faz os dois. Parece que não deveria ter um efeito negativo, mas alguém teve uma experiência ruim com essa prática de fazer o nível AR e DB?
James Klein
1
Nível de banco de dados é o que eu estava procurando. Esta deve ser a resposta aceita em minha opinião. Os outros parecem funcionar apenas se minhas consultas seguirem as operações padrão do ActiveRecord.
Brett Beatty
10

Apenas tenha em mente que delete_all não executará nenhum callback (como before_destroy e after_destroy) nos registros filho.

Jarin Udom
fonte
6

Parece que este plug-in pode fornecer o que você está procurando se quiser que as exclusões em cascata sejam refletidas na estrutura real do banco de dados:

http://www.redhillonrails.org/foreign_key_migrations.html

O formato para usar isso em uma migração seria mais ou menos assim:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end
Sean McMains
fonte
5
Esse link está morto, mas esta é uma alternativa mais recente: github.com/matthuhiggins/foreigner
gdelfino