I have a rails app that has two models one is vender and the other is purchase_request where vender has has_many :purchase_requests and purchase_request has belongs_to :vender yet when I call destroy it will not delete fields with this vender in it, and thus crashes the application because it returns nil objects where this vender use to be. how can I fix this?
It sounds like you need to use the :dependent => :destroy option for you association.
class Vendor < AR::Base
has_many :purchase_requests, :dependent => :destroy
end
Try this code, it should work for you:
has_many :purchase_requests, :dependent => :delete_all
Related
In my project there are many models with has_many association and dependant: :destroy flag. Also, each model have other belong_to associations with the dependant: :destroy flag. This models are nested between each other so when a destroy is executed on the top one, Rails triggers cascading destroy on children models.
Apart from that, models have callbacks that execute before_destroy.
The following represents what I described above:
class Model1Example < ActiveRecord::Base
has_many :model2_examples, :dependent => :destroy
belongs_to :other1_example, :dependent => :destroy
belongs_to :other2_example, :dependent => :destroy
end
class Model2Example < ActiveRecord::Base
belongs_to :model1_example
has_many :model3_examples, :dependent => :destroy
belongs_to :other3_example, :dependent => :destroy
belongs_to :other4_example, :dependent => :destroy
before_destroy :update_something_on_model1
before_destroy :check_some_inconsistence
end
class Model3Example < ActiveRecord::Base
belongs_to :model2_example
belongs_to :other5_example, :dependent => :destroy
belongs_to :other6_example, :dependent => :destroy
before_destroy :update_something_on_model2
before_destroy :check_some_inconsistence
end
Given that on average Model2Example holds about 100+ instances of Model3Example when the Model1Example destroy is triggered many SQL queries are triggered (10k+) because deletion is record by record and also all rules are executed for every instance...and this takes a lot more than what a user could wait for such a simple action.
I could fix this performance issue by using dependant: :delete_all on the has_many associations instead, because I don't really care that all this rules are executed when I trigger Model1Example destroy.
But the problem is that when I execute (from elsewhere in the app) a Model2Example destroy is in my interest that all rules are executed (specially Model3Example rules for each instance), and the previous mentioned approach brakes this.
Is there a "Rails way" to achieve a performance improvement for this case? Or should I just use SQL for Model1Example deletion?
Also, if I have to use this approach and I wanted to check some basic stuff before destroying Model1Example, where is the best place to do this validation? controller?
I'm trying to learn Rails has_many and belongs_to method to create collection of schedule.I read many information about this.And I can't understand :dependent => :destroy mean.
what does it works for?
:dependent => :destroy
This tells rails to delete all the child instances when a parent instance is deleted. Generally you want to do this if you don't want child instances hanging around when its parent has been deleted.
:dependent => :destroy means when you delete the parent object it's children objects are automatically deteted.
class Post < ActiveRecord::Base
has_many :posts_tags
has_many :tags, through: :posts_tags
end
class PostsTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :posts_tags
has_many :posts, through: :posts_tags
end
When Post gets destroyed I want all of its associations to Tag deleted as well. I do NOT want validations on PostsTag model to run. I just want to deleted.
I've found that adding a dependent on the relationship to posts tags from the Post model works as I want: has_many :posts_tags, dependent: :delete_all.
However, the documentation on the subject seems to suggest that I should do this instead: has_many :tags, through: :posts_tags, dependent: :delete_all. When I do this, the Tag object gets destroyed and the join object remains.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
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).
How can I have the default strategy actually used? If I leave :dependent off completely, no records are removed at all. And I cannot just indicate :dependent on a has_many relationship. Rails comes back and says "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict ({})".
If I don't specify :dependent on either of the relationships, it does NOT nullify the post_id on the PostsTag object as it seems to suggest
Perhaps I am reading this wrong and the approach that I found works is the correct way?
Your original idea of:
has_many :posts_tags, dependent: :delete_all
is exactly what you want. You do not want to declare this on the has-many-though association :tags, as that will destroy all associated Tags. What you want to delete is the association itself - which is what the PostTag join model represents.
So why do the docs say what they do? You are misunderstanding the scenario that the documentation is describing:
Post.find(1).destroy
Post.find(1).tags.delete
The first call (your scenario) will simply destroy the Post. That is, unless you specify a :dependent strategy, as I suggest you do. The second call is what the documentation is describing. Calling .tags.delete will not (by default) actually destroy the tags (since they are joined by has-many-through), but the associated join model that joins these tags.
I am trying to figure out how i can tell what has changed in an array in the after save callback. Here is an example of code i am using:
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy
has_many :subusers, :through => :user_maps, :dependent => :destroy
has_many :inverse_user_maps, :class_name => "UserMap", :foreign_key => "subuser_id"
has_one :parent, :through => :inverse_user_maps, :source => :user
after_save :remove_subusers
def remove_subusers
if self.subuser_ids_were != self.subuser_ids
leftover = self.subuser_ids_were - self.subuser_ids
leftover.each do |subuser|
subuser.destroy
end
end
end
end
class UserMap < ActiveRecord::Base
belongs_to :user
belongs_to :subuser, :class_name => "User"
end
I am removing the subusers with the after_save callback because i could not get the dependent destroy feature to work through user_maps. Does anyone have any ideas on a way to do this?
Thanks!
You can use the Dirty module accessors http://ar.rubyonrails.org/classes/ActiveRecord/Dirty.html as suggested in Determine what attributes were changed in Rails after_save callback?
In your case the handler you have for after_save will have access to subusers_change which is an array of two elements, first being the previous value and second being the new value.
although not strictly the answer to your question, I think you maybe able to get :dependent => :destroy working if you try the following...
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy
has_many :subusers, :through => :user_maps # removing the :dependent => :destroy option
end
class UserMap < ActiveRecord::Base
belongs_to :user
belongs_to :subuser, :class_name => "User", :dependent => :destroy # add it here
end
By moving the :dependent => :destroy option to the belongs_to association in the UserMap model you set up a cascading delete via the UserMap#destroy method. In other words, calling User#destroy will call UserMap#destroy for each UserMap record, which will in turn call sub_user.destroy for its sub_user record.
EDIT
Since the solution above didn't work, my next suggestion would be to add a callback to the user_maps association, however this comes with a warning that I will add after
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy, :before_remove => :remove_associated_subuser
def remove_associated_subuser(user_map)
user_map.subuser.destroy
end
end
WARNINGS
1) Using a before_remove callback will mean that the user_map.destroy function won't be called if there is an error with the callback
2) You will have to destroy your UserMap record using the method on the User class for example...
# this will fire the callback
u = User.first
u.user_maps.destroy(u.user_maps.first)
# this WONT fire the callback
UserMap.first.destroy
All things considered, this would make me nervous. I would first try modifying your code to make the associations a little less coupled to the same tables, so the :dependent => :destroy option can work, and if you can't do that, add a cascade delete constraint on to the database, at least then your associations will always be removed regardless of where / how you destroy it in your rails app.
Apparently dependent => destroy is ignored when also using the :through option.
So I have this...
class Comment < ActiveRecord::Base
has_many :comment_users, :dependent => :destroy
has_many :users, :through => :comment_users
...
end
...but deleting a Comment does not result in the associated comment_user records getting deleted.
What's the recommended approach, then, for cascade deletes when using :through?
Thanks
Apparently :dependent is not ignored!
The real issue was that I was calling Comment.delete(id) which goes straight to the db, whereas I now use Comment.destroy(id) which loads the Comment object and calls destroy() on it. This picks up the :dependent => :destroy and all is well.
The original poster's solution is valid, however I wanted to point out that this only works if you have an id column for that table. I prefer my many-to-many tables to only be the two foreign keys, but I had to remove my "id: false" from the migration table definition for cascading delete to work. Having this functionality definitely outweighs not having an id column on the table.
If you have a polymorphic association, you should do what #blogofsongs said but with a foreign_key attribute like so:
class User < ActiveRecord::Base
has_many :activities , dependent: :destroy, foreign_key: :trackable_id
end