How to remove the association after deleting a model object - ruby-on-rails

I have a simple has_many/belongs_to association between 2 of my models.
My problem is that when I delete an object that I no longer want (from the parent model), the foreign key within the child model's object remains. For example:
Forest
has_many :trees, :inverse_of => :forest
Tree
belongs_to :forest
When I delete a Forest object, all the associated Tree objects still contain a value for forest_id, which results in errors on the frontend. Is there some sort of hook like before_destroy that I can use? Though I'm thinking there's probably a very simple solution to this.
Thanks

I think that you want the :dependent option.
has_many :trees, :inverse_of => :forest, :dependent => :nullify
This will set the foreign keys to nil when the associated model is destroyed. You could also use :dependent => :destroy to destroy the model.
The documentation here might help.

You have add the :dependent option with the association like
has_many :trees, :dependent => :destroy
It will delete all trees of a particular Forest object.

Related

Delete/Destroy record on join_table with has_many through

I render a set of checkboxes and I want to delete all the Privilege of a User.
When I check one of the checkboxes, a record is created.
When I uncheck one of the checkboxes, a record is deleted.
I have searched all the questions related, but unluckily none works.
Rails 5.2.3
User
has_many :user_privileges, class_name: 'UserPrivileges'
has_many :privileges, through: :user_privileges
Privilege
has_many :user_privileges, class_name: 'UserPrivileges'
has_many :users, through: :user_privileges
UserPrivileges
belongs_to :user
belongs_to :privilege
The issue kick in when I want to delete ( uncheck ) the last privilege-record of that user in the join_table.
The record is still there, and there is no way to delete/destroy that specific record.
My intuition recall to the callbacks, I have tried different ways of using dependent but the last record is still there.
Any tips are welcome.
Thanks
If you want to delete the record from the join table, you need to add dependent: :destroy to has_many :through relationship.
# privilege.rb
has_many :user_privileges, class_name: 'UserPrivileges'
has_many :users, through: :user_privileges, dependent: :destroy
See What gets deleted? in API docs:
There is a potential pitfall here: has_and_belongs_to_many and
has_many :through associations have records in join tables, as well as
the associated records. So when we call one of these deletion methods,
what exactly should be deleted?
The answer is that it is assumed that deletion on an association is
about removing the link between the owner and the associated
object(s), rather than necessarily the associated objects themselves.
So with has_and_belongs_to_many and has_many :through, the join
records will be deleted, but the associated records won't.
To run dependent: :destroy callback, you must use the destroy or destroy_all method when deleting the privilege record.
See Delete or destroy? in API docs:
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 to do nothing (leave the foreign keys with the parent ids
set), except for has_many :through, where the default strategy is
delete_all (delete the join records, without running their callbacks).

Rails has_many and belongs_to method relationship

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.

Rails -- how to structure belongs_to/has_many associations?

Pretty simple question, I think: so I have a User model, a Product model, and a Comment model. I want the Users to be able to comment on specific products (like leave reviews for the products).
Is this the correct structure?
User
has_many :comments
Product
has_many :comments
Comment
belongs_to :user
belongs_to :product
Thanks.
yes it's correct if you want just comment on products, if you will comment on other model other than product, then use polymorphic association.
also don't forget to add dependent: :destroy to destroy related comments if the product is destroyed or the user is destroyed
in Product and User model add dependent: :destroy
has_many :comments, dependent: :destroy
if you want other behavior than this, there is other options, from Doc :
:dependent
Controls what happens to the associated objects when their owner is
destroyed:
: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 execute)
: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 object

Proper way to delete has_many :through join records?

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.

dependent => destroy on a "has_many through" association

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

Resources