Могу ли я настроить каскадное удаление в Rails?

88

Я знаю, что это, вероятно, где-то в Интернете, но я не могу найти ответ здесь, в Stackoverflow, поэтому я подумал, что могу немного расширить базу знаний здесь.

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

Мне было трудно изменить свое мышление, чтобы разрабатывать приложение на основе «модели», а не на основе базы данных, поэтому я пытаюсь понять, как бы я мог выполнять всю работу по проектированию, которую я обычно выполнял в базе данных Вместо этого модель Rails.

Итак, последняя задача, которую я дал себе, - это выяснить, как настроить модель базы данных Rails для выполнения каскадных удалений? Есть ли простой способ сделать это? Или мне нужно будет войти в MySql и настроить это?

matt_dev
источник

Ответы:

103

вы также можете установить опцию: independent на: delete_all. : delete_all выдаст один оператор SQL для удаления всех дочерних записей. из-за этого использование: delete_all может повысить производительность.

has_many :memberships, dependent: :delete_all
Майк Брин
источник
8
Ваше объяснение сбивает с толку. Будет использоваться один оператор SQL, но метод уничтожения не будет вызываться для каждой дочерней строки. Для этого вам нужно использовать destroy_all.
Джон Топли,
@John - надеюсь, правки прояснят путаницу. Спасибо что подметил это.
Майк Брин,
26
Убедитесь, что вы понимаете разницу между использованием :delete_allи :destroyдля этого. Оба будут вызывать дочернее членство (1 уровень для удаления [необходима цитата] и nдля уничтожения (если их дочерние элементы имеют зависимые уничтожения)) будут удалены из базы данных, но :destroyбудут создавать экземпляры каждого дочернего объекта и сначала запускать любые обратные вызовы, тогда как :delete_allбудет напрямую запускать Оператор SQL DELETE в базе данных. :destroyиз-за этого работает медленнее, но позволяет выполнять обратные вызовы при уничтожении записи. Обход Rails на одном конце и потенциальное создание n ^ x на другом.
jstim
2
Предлагаю также настроить внешние ключи базы данных. Таким образом, записи удаляются за одну операцию. См. Ответ ниже, который я опубликовал.
Хендрик
66

Да, можно, если вы используете отношения типа has_many, просто сделайте это

has_many :memberships, dependent: :destroy
Данмайер
источник
Дэн, я предполагаю, что мой следующий вопрос: если я запущу команду db migrate, действительно ли она настроена в базе данных? Или каскадирование полностью осуществляется по рельсам?
matt_dev 01
Да, по рельсам. (Тем не менее, убедитесь, что вам действительно всегда нужно удалять все связанные строки.)
Штейн Г. Стриндхауг, 01
@Matt - строка has_many должна быть в вашем классе модели, миграция не добавит этого за вас.
Гарет,
Я предпочитаю это решение, потому что оно также работает, если у зависимой модели есть другое отношение has_many
tpei
25

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

Как и в предложенном ответе, сделайте это:

has_many :memberships, dependent: :delete_all

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

Чтобы обнулить значения при удалении членства, если у вас есть модель пользователя:

add_foreign_key :users, :memberships, on_delete: :nullify

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

add_foreign_key :users, :memberships, on_delete: :cascade
Хендрик
источник
Итак, могу ли я использовать как «has_many: memberships, зависимые:: delete_all», так и «add_foreign_key: users,: memberships, on_delete:: cascade»? Будет ли работать хорошо?
Rubycon
2
Вам даже не нужно будет настраивать delete_allв модели. Внешний ключ позаботится о правильном удалении всего на уровне базы данных.
Хендрик
3
Мне любопытно, что происходит, когда вы делаете и то, и другое. Похоже, это не должно иметь отрицательного эффекта, но был ли у кого-нибудь плохой опыт использования этой практики как уровня AR, так и уровня DB?
Джеймс Кляйн
1
Уровень базы данных - это то, что я искал. На мой взгляд, это должен быть принятый ответ. Остальные, похоже, работают, только если мои запросы придерживаются стандартных операций ActiveRecord.
Brett Beatty
10

Просто имейте в виду, что delete_all не будет выполнять никаких обратных вызовов (например, before_destroy и after_destroy) для дочерних записей.

Джарин Удом
источник
6

Похоже, этот плагин может дать вам то, что вы ищете, если вы хотите, чтобы каскадные удаления отражались в фактической структуре базы данных:

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

Формат для использования этого при миграции будет примерно таким:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end
Шон МакМейнс
источник
5
Эта ссылка мертва, но это более новая альтернатива: github.com/matthuhiggins/foreigner
gdelfino