dependent destroy not working - ruby-on-rails

I'm trying to use dependent: :destroy without success.
Lets put a simple example. I create a simple application with the following:
rails g model parent
rails g model child parent:references
Add following lines to parent.rb
has_many :children, dependent: :destroy
I do the following test in rails console (rails c)
p = Parent.create!
c = Child.create!
c.parent = p
c.save
#check association
Child.first == Child.first.parent.children.first
p.delete
#This should return 0
Child.count == 0
And Child.count returns 1.
What I'm missing?
Thanks

4.2.2.4 :dependent
If you set the :dependent option to:
:destroy, when the object is destroyed, #destroy will be called on its associated objects.
:delete, when the object is destroyed, all its associated objects will be deleted directly from the database without calling their #destroy method.
As per your settings, you have to do p.destroy.
The :dependent option can have different values which specify how the deletion is done. For more information, see the documentation for this option on the different specific association types. When no option is given, the behaviour is to do nothing with the associated records when destroying a record.
For has_many, destroy and destroy_all will always call the destroy method of the record(s) being removed so that callbacks are run. However delete and delete_all will either do the deletion according to the strategy specified by the :dependent option, or if no :dependent option is given, then it will follow the default strategy. The default strategy is :nullify (set the foreign keys to nil), except for has_many :through, where the default strategy is delete_all (delete the join records, without running their callbacks).

Calling the delete method on an ActiveRecord derived object will issue a direct DELETE statement do the database, skipping any ActiveRecord callbacks and configurations such as dependent: destroy.
I believe you want the destroy method.
You could also set up a foreign key in the database and set it to cascade on delete, it might make sense, depending on your needs.

Related

ActiveRecord delete_all method updating instead of deleting

I'm using Rails polymorphic associations, that way some models have many cash_histories children, like this:
has_many :cash_histories, as: :cashable
But when I try to delete all cash histories from a parent #resource, like this:
#resource.cash_histories.delete_all
I get the following query:
UPDATE "cash_histories" SET "cashable_id" = NULL WHERE "cash_histories"."cashable_id" = $1 AND "cash_histories"."cashable_type" = $2 [["cashable_id", 1], ["cashable_type", "ServiceOrder"]]
I can't understand this behavior, setting relationship id to null instead of removing, that will result in dead rows in my table. Why is that happening?
I'm using Rails 4.1.
From the Rails API docs for delete_all:
Deletes all the records from the collection. For has_many associations, the deletion is done according to the strategy specified by the :dependent option. Returns an array with the deleted records.
If no :dependent option is given, then it will follow the default strategy. The default strategy is :nullify. This sets the foreign keys to NULL. For, has_many :through, the default strategy is delete_all.
So you you just need to set the :dependent option on your has_many to either :delete_all or :destroy, depending on what behavior you want.
has_many :cash_histories, as: :cashable, dependent: :delete_all
From the Rails API docs for has_many:
Objects will be in addition destroyed if they're associated with dependent: :destroy, and deleted if they're associated with dependent: :delete_all.
its still weird because everywhere else it always describes what happens when the owner is destroyed.
Controls what happens to associated objects when their owner is
destroyed:
:destroy causes the associated objects to also be destroyed.
:delete_all causes the associated objects to be deleted directly from the database (callbacks are not executed).
:nullify causes the foreign keys to be set to NULL (callbacks are not executed).
:restrict_with_exception causes an exception to be raised if there are associated records.
:restrict_with_error causes an error to be added to the owner if there are associated objects.

In rails, How to determine if a record was destroyed by a dependent: :destroy callback?

I've got a record in my Rails app with an after_destroy hook that needs to be aware of why the record gets destroyed. More specifically, if the record is being destroyed in a cascade because its parent says dependent: :destroy, it needs to do things differently than if the record was individually destroyed.
What I tried to do is to see if its parent was destroyed?, only to figure out that dependent: :destroy callbacks are done before the parent is destroyed. Which makes sense because it should be able to fail. (i.e. restrict).
So, how do I do this?
Solution #1
If your model is simple enough and you don't need to invoke any callbacks in the child relation, you can just use dependent: delete_all in the parent.
Solution #2
For more complex scenarios you can use destroyed_by_association, which returns a ActiveRecord::Reflection::HasManyReflection object when it's part of cascade, or nil otherwise:
after_destroy :your_callback
def your_callback
if destroyed_by_association
# this is part of a cascade
else
# isolated deletion
end
end
I just tried this in Rails 4.2 and it works.
Source: https://github.com/rails/rails/issues/12828#issuecomment-28142658
One way to do this is using the before_destroy callback in the parent object to mark all child objects as destroyed through parent destroy. Like this:
class YourClass
before_destroy :mark_children
...
...
def mark_children
[:association1, :association2].each do |association| # Array should include association names that hate :dependent => :destroy option
self.send(association).each do |child|
# mark child object as deleted by parent
end
end
end
end
You can also use ActiveRecord reflections to determine automatically which associations are marked as :dependent => :destroy. Doing this is helpful when you need this function in many classes.

How to remove a record without destroying it?

I am using Mongoid and I have 2 models, Flow and Node with a referenced parent-child relationship.
class Node
belongs_to :flow
end
class Flow
has_many :nodes
end
When I want to remove a node with a flow I do this:
flow.nodes.clear
This destroy the associated nodes. What if I want to remove the association between the node and the flow without destroying the associated nodes? Is there a way of doing that?
You should be able to use flow.nodes.clear as long as you don't have :dependent => :destroy set. From the Rails Guide on Association Basics:
4.3.1.7 collection.clear
The collection.clear method removes every object from the collection.
This destroys the associated objects if they are associated with
:dependent => :destroy, deletes them directly from the database if
:dependent => :delete_all, and otherwise sets their foreign keys to
NULL.
If this isn't working for you, you could try this and it should remove the association:
flow.nodes = nil
EDIT 1
If not, you'll have to create a method to remove the association manually.
flow.nodes.update_all :flow_id => nil
I don't believe there's any in built method for this, but you can do this:
Node.where(:flow_id => flow.id).update_all(:flow_id => nil)

What are the default values for Rails 3 for :dependent on has_many and belongs_to

In rails 3, i know that i can force deletion of dependent objects on belongs_to and has_many relations using the :dependent => :delete option. However i was wondering,
what is the default behavior if i do not specify :dependent => ...
Cheers,
Hajo
The documentation says, "When no option is given, the behavior is to do nothing with the associated records when destroying a record." That is, deleting or destroying an object will not delete or destroy the objects that it belongs to or has many of.
has_many uses the :nullify strategy, which will set the foreign to null. For has_many :through it will use delete_all.
For has_many, destroy will always call the destroy method of the
record(s) being removed so that callbacks are run. However delete will
either do the deletion according to the strategy specified by the
:dependent option, or if no :dependent option is given, then it will
follow the default strategy. The default strategy is :nullify (set the
foreign keys to nil), except for has_many :through, where the default
strategy is delete_all (delete the join records, without running their
callbacks).
-- ActiveRecord::Associations::ClassMethods
Not sure exactly what belongs_to does, and wasn't able to find anything in the docs. I'll try to do some digging soon and update the answer.
In Rails 3, the default :dependent value is :nullify which sets foreign keys to nil.
The default strategy is :nullify for regular has_many. Also, this only works at all if the source reflection is a belongs_to.
Source: http://guides.rubyonrails.org/3_1_release_notes.html#active-record
This is still the case in Rails 4.
However delete and delete_all will either do the deletion according to the strategy specified by the :dependent option, or if no :dependent option is given, then it will follow the default strategy. The default strategy is :nullify (set the foreign keys to nil), except for has_many :through, where the default strategy is delete_all (delete the join records, without running their callbacks).
Source: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Delete+or+destroy%3F
Also see the source code docs: https://github.com/rails/rails/blob/b5a8fd7bb4a6fa4b67d4eabae4cea2cb1834d8d9/activerecord/lib/active_record/associations/collection_proxy.rb#L369

Rails :dependent => :destroy VS :dependent => :delete_all

In rails guides it's described like this:
Objects will be in addition destroyed if they’re associated with :dependent => :destroy, and deleted if they’re associated with :dependent => :delete_all
Right, cool. But what's the difference between being destroyed and being deleted?
I tried both and it seems to do the same thing.
The difference is with the callback.
The :delete_all is made directly in your application and deletes by SQL :
DELETE * FROM users where compagny_id = XXXX
With the :destroy, there is an instantiation of all of your children. So, if you can't destroy it or if each has their own :dependent, its callbacks can be called.
On a Rails' model association you can specify the :dependent option, which can take one of the following three forms:
:destroy/:destroy_all The associated objects are destroyed alongside this object by calling their destroy method
:delete/:delete_all All associated objects are destroyed immediately without calling their :destroy method
:nullify All associated objects' foreign keys are set to NULL without calling their save callbacks
See destroy deletes its associated elements where delete_all can delete multiple data from self table as DELETE * FROM table where field = 'xyz'
:Dependent possible options:
Controls what happens to the associated objects when their owner is destroyed. Note that these are implemented as callbacks, and Rails executes callbacks in order. Therefore, other similar callbacks may affect the :dependent behavior, and the :dependent behavior may affect other callbacks.
:destroy causes all the associated objects to also be destroyed.
:delete_all causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
:nullify causes the foreign keys to be set to NULL. Callbacks are not executed.
:restrict_with_exception causes an exception to be raised if there are any associated records.
:restrict_with_error causes an error to be added to the owner if there are any associated objects.
If using with the :through option, the association on the join model must be a belongs_to, and the records which get deleted are the join records, rather than the associated records.
Actually the main difference is that any callbacks will not be invoked when :delete_all was used. But when used :destroy the callbacks stack (:after_destroy, :after_commit ...) will be fired.
Consequently, if you have touch:ing declarations in models being deleted then it's better to use dependent: :delete_all rather 'dependent: :destroy'.

Resources