Вам просто нужно настроить внешние ключи для каскадирования, а Postgres позаботится об удалении до конца строки. Поскольку это реализовано на уровне базы данных, не имеет значения, как вы запускаете удаление из Rails.
class CreateCountries < ActiveRecord::Migration[6.0]
def change
create_table :countries do |t|
t.string :name
t.timestamps
end
end
end
class CreateStates < ActiveRecord::Migration[6.0]
def change
create_table :states do |t|
t.string :name
t.belongs_to :country, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
class CreateCities < ActiveRecord::Migration[6.0]
def change
create_table :cities do |t|
t.string :name
t.belongs_to :state, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
Модели:
class Country < ApplicationRecord
has_many :states
has_many :cities, through: :states
end
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
class City < ApplicationRecord
belongs_to :state
has_one :country, through: :state
end
Передача spe c:
require 'rails_helper'
RSpec.describe Country, type: :model do
describe "cascading delete" do
let!(:country){ Country.create }
let!(:state){ country.states.create }
let!(:city){ state.cities.create }
it "deletes the states" do
expect {
country.delete
}.to change(State, :count).from(1).to(0)
end
it "deletes the cities" do
expect {
Country.delete_all
}.to change(City, :count).from(1).to(0)
end
end
end
Если вы используете .each_with_batches
или нет здесь, это не имеет значения здесь. Все, что создает запрос DELETE FROM countries
, будет запускать этот триггер базы данных. Если вам действительно не нужно оценивать, следует ли удалять каждого родителя в Rails, вы должны просто сделать:
Country.where(evil: true).delete_all
Это будет гораздо эффективнее, чем .find_each
, поскольку вы просто делаете это SQL запрос. Если вы перебираете записи, вы делаете один DELETE FROM coutries WHERE id = ?
запрос на строку и, поскольку его блокирующий Rails должен ждать обратного пути к БД.