我有很多要删除的数据,因此,我通过级联外键(on_delete: :cascade
)使用delete / delete_all而不是destroy。
我想删除一个父活动记录,该记录有几个带有许多行的“子表”。这些子表中的一些也有几个子表。因此,我在外键上添加了层叠,因此我只需要调用parent.delete即可触发删除所有父级的所有子级和大级子级。
我想将delete / delete_all与活动记录批处理https://api.rubyonrails.org/classes/ActiveRecord/Batches.html结合起来,但是由于只有一个单亲,我不确定如何以一种简洁的方式结合批处理和级联删除。
一种选择是显式批量删除子代和大子代,例如
parent.children_x.find_each do |child_x|
child_x.grand_children_y.in_batches do |grand_child_y_batch|
grand_child_y_batch.delete_all
end
child_x.delete
end
parent.children_z.in_batches do |child_batch|
child_batch.delete_all
end
...etc...
但是,如果有一种更隐式的方式允许我仅在父级上调用delete并批量删除子级和大级子级,那是可取的,例如
parent.cascade_in_batches do |parent_batch|
parent_batch.delete_all #This batch deletes all children and grand children
end
[我看到父级上没有in_batches
,因为父级只是一个实体,所以看起来只有如我在上面的第一个示例中那样显式删除时才有可能吗?
谢谢,
-路易丝
您实际上只需要设置外键进行级联,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
通过规格:
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必须等待往返数据库的时间。