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 withdependent: :destroy
, and deleted if they're associated withdependent: :delete_all
.If the
:through
option is used, then the join records are deleted (rather than nullified) by default, but you can specifydependent: :destroy
ordependent: :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.
yes, you can use
dependent: :destroy
andon 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 likevalidates :some_field, uniqueness: true
that is better to be backed by unique index, only database itself can ensure data consistency.since parent is deleted last,
on delete nullify
will not get in the way (you'll get nullified race condition records)there's transaction wrapping all deletes, only race condition records can be left over
on delete restrict
overdependent: :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
Rspec 'Eq' VS 'Eql' in 'Expect' Tests
Ruby Scope of Data After _End_
How to Read a Gzip File Line by Line
Devise Install from Existing Model/Database
Alphabetize Arabic and Japanese Text That Is in Unicode
How to Modify a Text File in Ruby
How to Run "Bundle Exec Jekyll New ."
Rails Generate Error: No Such File or Directory - Getcwd
Call Before Methods in Model on Ruby
In Ruby, How to Find Out If a String Is Not in an Array
Using Activerecord Find_In_Batches Method for Deleting Large Data
Split Seeds.Rb into Multiple Sections
Yielding in an Anonymous Block
Generate Array of Numbers That Fit to a Probability Distribution in Ruby
In Ruby How to Automatically Populate Instance Variables Somehow in the Initialize Method
Your Ruby Version Is 2.2.4, But Your Gemfile Specified 2.3.0