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
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
Ok thanks, your answer helped me a lot. A couple of follow-up questions: a) in the down method the remove and add methods should be switched right? b) ok I see the dependent: :delete_all works if I initiate it with destroy, but it does only work on "one level", i.e. any children order_items might have will not be deleted, because the callback is not triggered by the delete on order_items as you explain. For that reason I will need to use cascade on the foreign keys.
A) yes you're right - the order is wrong. B) yes. Also right. The associated records are removed with the
.delete_all
method which just creates a single SQL delete query and does not instanciate the models or call callbacks.The advantage of cascade inte this case is that it cascades so the children and grandchildren will be deleted if you setup the foreign keys properly.