Reputation:
I have the following classes
class Market < ActiveRecord::Base
has_many :products
has_many :categories, through: :products
end
class Product < ActiveRecord::Base
belongs_to :market
belongs_to : category
end
class Category < ActiveRecord::Base
has_many :products
has_many :markets, through: :products
end
Now with the following code:
market = .. #some market
market.categories.destroy_all
market products are deleted as well! How to prevent this ?
Edit
Categories don't get deleted in the first place, but only products! And using market.categories.delete_all
only delete products in one query instead of many, but again categories aren't deleted! It seems this is the way rails handle this situation of relationships, to destroy products such that market isn't related to categories anymore, so it has the illusion of deleting them! And hence any destroy callback on category isn't called, as category isn't deleted!
Upvotes: 1
Views: 542
Reputation:
Ok I think I know the answer: Category and Market are a many-to-many relation ship, so when you say market.categories.delete_all
(or destroy_all) it tries to break the relation between this market and its categories, which means deleting the join records, which are produtcs records in our case.
Upvotes: 0
Reputation: 52357
You have to understand the difference between delete_all
and destroy_all
.
The first one will simply execute the SQL DELETE.
Docs on delete_all
:
Deletes the records matching conditions without instantiating the records first, and hence not calling the destroy method nor invoking callbacks. This is a single SQL DELETE statement that goes straight to the database, much more efficient than destroy_all. Be careful with relations though, in particular :dependent rules defined on associations are not honored. Returns the number of rows affected.
From docs on destroy_all
:
Note: Instantiation, callback execution, and deletion of each record can be time consuming when you’re removing many records at once. It generates at least one SQL DELETE query per record (or possibly more, to enforce your callbacks). If you want to delete many rows quickly, without concern for their associations or callbacks, use delete_all instead.
So to not execute callbacks you should resort to delete, not destroy. On the contrary, destroy is preferable when you have callbacks.
Upvotes: 2
Reputation: 3869
market = .. #some market
market.categories.delete_all
But deleting categories will raise problems for products records. The better way using destroy_all
and in the before_destroy
callback set category_id = nil
for products.
Upvotes: 0