Is there any efficient way to delete all associated records which where created before adding dependant: :destroy ??
Class User
end
Class Post
belongs_to :user, dependent: :destroy
end
Previous records which were created before adding this dependent: :destroy are still present and have user_id present.
eg.
Post.first.user_id = 1
But User.first which has id of 1 is already destroyed before adding the dependent: : destroy.
How do i find and delete these Post records???
Post.where("NOT EXISTS(select 1 from #{User.table_name} where #{Post.table_name}.user_id=#{User.table_name}.id)").delete_all
This should delete all the posts whose associated user no longer exists in the db.
If you want to trigger the callbacks for each Post before deleting them use destroy_all instead.
See: delete_all vs destroy_all? for the difference.
Related
Intended functionality - delete all linked asset_items when an asset_line_item is deleted. (without using destroy, destroy_all). I am using postgres
With the following model:
class AssetLineItem < PurchaseLineItem
has_many :asset_items
...
after_destroy :destroy_cleanup
private
def destroy_cleanup
asset_items.delete_all
end
end
This results in the asset_items remaining, however all of their asset_line_item columns are set to null.
def destroy_cleanup
asset_items.each do |asset_item|
asset_item.delete
end
end
replacing delete_all with the loop above however has the intended result of delete all associated asset_items.
Although I have working code, I'm curious what could cause delete_all to act in this way?
Calling just delete_all on association just nullifies the reference. It is the same as delete_all(:nullify):
pry(main)> Booking.last.passengers.delete_all
Booking Load (0.6ms) SELECT `bookings`.* FROM `bookings` ORDER BY `bookings`.`id` DESC LIMIT 1
SQL (2.8ms) UPDATE `passengers` SET `passengers`.`booking_id` = NULL WHERE `passengers`.`booking_id` = 157
=> nil
You need to call delete_all(:delete_all) to actually delete associated records.
Here is docs.
Or to get desired effect you can add following line to your AssetLineItem model:
has_many :asset_items, dependent: :destroy
as lakhvir kumar mentioned.
Also your destroy_cleanup callback could be refactored to:
def destroy_cleanup
asset_items.map(&:delete)
end
Here is some good links to the topic:
delete_all vs destroy_all?
Rails :dependent => :destroy VS :dependent => :delete_all
use has_many :asset_items dependent: :destroy
I have this code:
class FlexField < ActiveRecord::Base
has_many :flex_field_values, class_name: 'FlexFieldValue'
after_save :delete_flex_values
def delete_flex_values
if self.field_type != 'list'
self.flex_field_values.delete_all
end
end
The goal is to delete all values if the type isn't a list. Now what is happening is that as soon as I set the type to something other than list, none of the children get deleted, but their flex_field_id gets set to null.
How can I really have them deleted?
You can write as :
class FlexField < ActiveRecord::Base
has_many :flex_field_values, class_name: 'FlexFieldValue', dependent: :destroy
after_save :delete_flex_values
def delete_flex_values
if self.field_type != 'list'
self.flex_field_values.clear
end
end
end
A brief idea about collection.clear:
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, otherwise sets their foreign keys to NULL. If the :through option is true no destroy callbacks are invoked on the join models. Join models are directly deleted.
I've a Rails 4 app that uses Postgresql database. I'm using UUIDs as id for my models.
Everything works as expected, I'm trying to set a dependant destroy has many relation, and the "dependant destroy" is not working.
Is there any incompativility between postgress UUIDs and dependent destroy? I need to set foreign keys?
I expalin a bit of my code:
Navigation through models is working correclty
To define the has_many I'm using
has_many :some_models, dependent: :destroy
My migrations are something like:
def change
create_table :my_model, id: :uuid do |t|
To test, I'm using console. I create a relation, delete the "some_models" and the main model is not deleted.
Thanks
You are thinking of the association backwards. dependent: destroy means: When I destroy a parent record, destroy the children that are associated with that record. Here's a contrived example:
class User
has_many :photos, dependent: :destroy
end
When the user is deleted, you want their photos to also be deleted.
If you really want to delete a parent record when a child is deleted, you can do so from the before_destroy callback like so:
class Photo
before_destroy :delete_parent_user
def delete_parent_user
user.destroy if self.user
end
end
Note that other children may still be pointing to that parent record if this is a has_many relationship so this may not be advisable.
dependent: :destroy only destroys child records. When you destroy my_model record, all some_model records belonging to it will be destroyed.
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.
I have a has_many :through model that works perfectly.
has_many :varietals
has_many :grapes, :through => :varietals, :dependent => :destroy
I would like to call another action instead of :destroy. In fact, I don't want to nullify the item OR destroy it, I want to update the record status field from 1 to 0 instead of destroy the record.
How to call a custom method instead of destroy ? I suppose I can do that in the model itself... Thanks.
Where to put this method ? In the master model or in the model where the record will be destroyed ?
EDIT:
I'm sorry but I think I didn't enough explain my problem. My problem is not only to so something after the master model is destroyed. I want to custom the destroy action in the Varietal model itself even if the master record is not destroyed.
Something like:
class Varietal < ActiveRecord::Base
private
def destroy
self.update_attributes(:status => 0)
end
end
Actually this action is not called...
You can use before_destroy to put your custom logic there. E.g.,
before_destroy :reset_status
def reset_status
...
end
Check here for more details.
You just need add a callback on before_destroy or after_destroy and manipulate your associations. By example
after_destroy :do_on_grapes
def do_on_grapes
grapes.map(&:to_do)
end
has_many :dependent is limited to only a few options. According to the documentation:
:dependent If set to :destroy all the associated objects are destroyed
alongside this object by calling their destroy method. If set to
:delete_all all associated objects are deleted without calling their
destroy method. If set to :nullify all associated objects’ foreign
keys are set to NULL without calling their save callbacks. If set to
:restrict this object raises an ActiveRecord::DeleteRestrictionError
exception and cannot be deleted if it has 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.
It looks like you would need to alter the destroy method to update the status field.
I believe that good approach to solve your problem is to provide a custom destroy method. There are several responses to questions like these, but you should keep in mind that ActiveRecord and Relationships like:
class Image < ActiveRecord::Base
has_many :comments, dependent: :destroy
use callback mechanisms that trigger destroy chaining to your relations, too. Usually you should preserve this mechanism and add it to your custom implementation. E.g.
def destroy
self.update deleted_at: Time.now
run_callbacks :destroy
end
You can read this post, too:
Triggering dependent: :destroy with overridden destroy-method