Warm tip: This article is reproduced from stackoverflow.com, please click
activerecord ruby-on-rails associations cascading-deletes

Deleting rails active records with delete_all/delete violates foreign key constraint

发布于 2020-03-29 20:59:32

I have an active record association setup with :dependent => :destroy, which works as intended. Then I found out I need to use delete instead of destroy due to performance, so I just changed destroy to delete_all/delete depending on the association.

When I try to delete:

shop.shop_snapshots.completed.last.delete

I get the error message:

ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR:  update or delete on table "shop_snapshots" violates foreign key constraint "fk_rails_c24b24adaf" on table "inventory_items"

But why is that - I believe I have the proper setup on the snapshot:

has_many :inventory_items, :dependent => :delete_all

and it worked for destroy, so what am I doing wrong?

Thanks /Louise

Questioner
Lull
Viewed
430
max 2020-02-03 14:07

On Postgres you can use the CASCADE option on the foreign key itself.

CASCADE specifies that when a referenced row is deleted, row(s) referencing it should be automatically deleted as well.
- https://www.postgresql.org/docs/9.5/ddl-constraints.html

This is usually setup when creating the table but you can add it to an exisiting table by removing and then re-adding the foreign key constraint:

class AddCascadeToOrderItems < ActiveRecord::Migration[6.0]
  def up
    remove_foreign_key :order_items, :orders
    add_foreign_key :order_items, :orders, on_delete: :cascade
  end

  def down
    remove_foreign_key :order_items, :orders
    add_foreign_key :order_items, :orders
  end
end

Since this is handled on the DB level no configuration is needed in your model.

has_many :inventory_items, dependent: :delete_all

Works as well and is the only option on peasant databases like MySQL but it will only be fired when you call .destroy and not .delete on the model that declares the association as its implemented as a model callback. For example:

class Store < ApplicationRecord
  has_many :inventory_items, dependent: :delete_all
end

store = Store.find(1)
store.destroy # triggers callbacks and will delete all assocatiated inventory_items
store.delete  # will not trigger callbacks