Rails - Aciverecord Use :Dependent => :Destroy on Condition

Rails - AciveRecord use :dependent = :destroy on condition

No. You should remove :dependent => :destroy and add after_destroy callback where you can write any logic you want.

class Worker < ActiveRecord::Base
has_many :jobs
has_many :coworkers
has_many :company_credit_cards
after_destroy :cleanup

private
def cleanup
if self.is_fired?
self.jobs.destroy_all
self.coworkers.destroy_all
self.company_credit_cards.destroy_all
end
end
end

Rails :dependent = destroy with conditions

You don't want to use :dependent => :destroy here, but rather the before_destroy callback like so:

#leash.rb

before_destroy :destroy_dog

def destroy_dog
dog.destroy unless dog.owner
end

Rails: How to use dependent: :destroy in rails?

Add cascading delete to your EmpGroup model:

class EmpGroup < ActiveRecord::Base
has_many :emp_group_members, dependent: :delete_all
end

Or

Are you calling delete method? you should call destroy instead.
Use .destroy

has_many through association dependent destroy under condition of who called destroy

Just say:

class Physician < ActiveRecord::Base
has_many :appointments, dependent: :restrict_with_exception
has_many :patients, through: :appointments
end

Note the dependent: :restrict_with_exception. This will cause Active Record to refuse to destroy any Physician records that have associated Appointment records.

See the API docs and the association basics guide.

Dependent: :destroy only to the 2nd degree in has many through association

You can just delete the items, just add dependent: :destroy to has_many :items.

class Collection < ApplicationRecord
has_many :searches, through: :items
has_many :items, dependent: :destroy
end

After destroying a collection, the item is destroyed but the search will remain.

second solution

You could even apply dependent: :nullify to has_many :searches - getting the same result.

class Collection < ApplicationRecord
has_many :searches, through: :items, dependent: :nullify
has_many :items
end

From the docs:

collection.delete(object, …)

Removes one or more objects from the collection by setting their foreign keys to NULL. Objects will be in addition destroyed if they're associated with dependent: :destroy, and deleted if they're associated with dependent: :delete_all.

If the :through option is used, then the join records are deleted (rather than nullified) by default, but you can specify dependent: :destroy or dependent: :nullify to override this.

Difference between dependent destroy vs dependent delete on Rails Active Record

Yes, both will delete the database records but doing it in a different way.

You can check the answer for this question here:

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

Basically dependent: :delete will execute the delete for the dependent records directly on the database without executing any activerecod validations or callbacks.

While dependent: :destroy will instantiate all the dependent records and execute a :destroy for each object (executing validations and callbacks).

Rails: dependent: :destroy - How does this work?

You probably want :nullify. See the Rails docs for has_many.

:dependent 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.

How it works - `belongs_to :user, dependent: :destroy`

"has_many" 

A teacher "has_many" students. Every student has only one teacher, but every teacher has many students. This means that there is a foreign key, or teacher_id on the student referencing to what teacher they belong to.

"belongs_to" 

A student "belongs_to" a teacher. Every teacher has many students, but every student has only one teacher. Again, there is the foreign key on the student referencing to what teacher they belong.

Let's work this out an using this student / teacher concept.

Teacher Model

class Teacher < ActiveRecord::Base
has_many :students, dependent: :destroy
end

Student Model

class Student < ActiveRecord::Base
belongs_to :teacher
end

Assuming these models then

Teacher.destroy 

Will delete the instantiated teacher and all the students that were associated to that teacher.

For example

Teacher.find(345).destroy 

Would destroy the record of the teacher with the ID of 345 and destroy all the associated students with that teacher.

Now to the heart of the question, what happens when my models look like this?

Teacher Model

class Teacher < ActiveRecord::Base
has_many :students, dependent: :destroy
end

Student Model

class Student < ActiveRecord::Base
belongs_to :teacher, dependent: :destroy
end

If I were to call

Student.destroy

This would destroy the instantiated student and that student's associated teacher. However to my knowledge (and according to the docs) this would not destroy any other students related to that teacher, leaving them 'orphaned'.

Here is a quote from the Ruby docs on this 1

If set to :destroy, the associated object is destroyed when this object is. This option should not be specified when belongs_to is used in conjunction with a has_many relationship on another class because of the potential to leave orphaned records behind.

:dependent = :destroy doesn't work on has_one relation

Your logic is fine, but it's your test code that's the problem. Try this:

test "associate object should be destroyed" do
user_detail = @user.user_detail
@user.destroy
expect(UserDetail.find_by(:user_id => @user.id)).to be_nil
end

What's happening is the database row that corresponds to user_detail is being destroyed, but the variable still holds the value.

edit to respond to comment since I can't put the code block in the comment:

@user.id isn't nil because, if you test it, you'll see that the id is still preserved because it's the in memory model. I have a random rails app I'm testing, here's some console output:

irb(main):002:0> l = Loan.first
irb(main):003:0> l.destroy
irb(main):004:0> l.id
=> 14

What Does Rails Do With Both :dependent = :destroy and cascade delete/nullify/restrict

With dependent: :destroy in a transaction rails first destroys all dependencies, and only then deletes the record itself.

There may be a race condition: if a dependent record was added just after rails read collection for destroying, but not deleted parent yet - it may be left over. Let's call these "race condition records" below.

  1. yes, you can use dependent: :destroy and on delete cascade, this way some children (race condition ones) can be deleted without callbacks. If callbacks are mandatory - on delete restrict together with some locking and explicit children deletion may be better.
    This is somewhat like validates :some_field, uniqueness: true that is better to be backed by unique index, only database itself can ensure data consistency.

  2. since parent is deleted last, on delete nullify will not get in the way (you'll get nullified race condition records)

  3. there's transaction wrapping all deletes, only race condition records can be left over

  4. on delete restrict over dependent: :destroy will trigger only for race condition records (and roll back whole transaction), but if there was no race condition - rails will happily delete everything.



Related Topics



Leave a reply



Submit