Reputation: 6674
I have a recipe portal and these recipes can have tags.
class Recipe < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings, dependent: :destroy
end
class Tag < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :recipes, through: :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :recipe
end
...when I delete a recipe, I'd like to delete the tag if the recipe being deleted is the only recipe with this tag.
class Recipe < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings, dependent: :destroy
before_destroy :remove_tags
private
# I need to pass an individual recipe
def remove_tags
if self.tags.present?
self.tags.each do |tag|
Recipe.tagged_with(tag).length == 1 ? tag.delete : next
# tagged_with() returns recipes with the given tag name
end
end
end
end
This function would work but I can't access the tags. How do I access the tags for the recipe being deleted?
Upvotes: 1
Views: 268
Reputation: 575
You are accessing the tags of the recipe but you are not seeing anything becase dependant_destroy
is executed before the actual destroy of the Recipe object.
If you inspect carefully the queries launched you will see that right before your callback, the DELETE FROM "taggings" . . .
is executed, so when you try to access the tags of the Recipe it returns an empty array.
Because you don't want to destroy tags everytime you destroy a recipe but only when is the only one you should remove your dependant_destroy
and place the logic in a after_destroy
so the resulting code would be:
class Recipe < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
after_destroy :remove_tags
private
# I need to pass an individual recipe
def remove_tags
if self.tags.present?
self.tags.each do |tag|
Recipe.tagged_with(tag).length == 1 ? tag.delete : next
# tagged_with() returns recipes with the given tag name
end
end
end
end
Upvotes: 2