I have three models. A collection has many searches through items.
When a collection is destroyed, I'd like its items to be destroyed, but searches to be nullified, since they are still valid objects on their own.
Can this be done?
Here are my models:
class Collection < ApplicationRecord
has_many :searches, through: :items
has_many :items
has_many :searches, through: :suggestions
has_many :suggestions
end
class Item < ApplicationRecord
belongs_to :collection
belongs_to :search
end
class Search < ApplicationRecord
has_many :collections, through: :items
has_many :items
has_many :collections, through: :suggestions
has_many :suggestions
end
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 with dependent: :destroy, and deleted if they're associated with dependent: :delete_all.
If the :through option is used, then the join records are deleted (rather than nullified) by default, but you can specify dependent: :destroy or dependent: :nullify to override this.
Related
I have 3 models.
Story, Tag and Tagging.
A Story has many tags through taggings. Here are my models:
**Story.rb**
class Story < ApplicationRecor
has_many :taggings, dependent: :delete_all
has_many :tags, through: :taggings
end
**Tagging.rb**
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :story
end
**Tag.rb**
class Tag < ApplicationRecord
has_many :taggings
has_many :stories, through: :taggings
end
So when I delete a story, I have dependent: :delete_all on taggings which calls a single SQL delete statement on all taggings associated with a Story. I'd like to also delete all Tags if there are no longer any taggings associated to it. For example, a story has 1 tagging and one tag through the tagging. When I delete that story, that story and tagging are deleted but that tag still remains. I'd like that tag to be removed as well since there are no taggings associated to that tag anymore.
I've tried this:
**Story.rb**
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
and this:
**Story.rb**
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings, dependent: :destroy
Both doesnt work.. Any advice on how to handle this?
You can check the tags using an after_destroy
class Tagging < ApplicationRecord
after_destroy :destroy_unused_tag
private
def destroy_unused_tag
tag.destroy if tag.taggings.empty?
end
end
Your Tagging model might be missing dependant option for tags.
**Story.rb**
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
**Tagging.rb**
belongs_to :story
belongs_to :tags, dependent: :destroy
I have a "LineOfBusiness" model which has an irregular pluralization ("lines_of_business" instead of "line_of_businesses").
I've set up my "has_many through" associations and made what I thought were the necessary adjustments for the irregular name, but I'm not sure why I'm having to use the plural form of the IDs in the strong params. The singular "user_ids" works in the params as I expected, but I have to use the plural "lines_of_business_ids" to get them to work properly. It seems like something small is off, but I can't figure it out.
class Question < ApplicationRecord
has_many :user_questions, dependent: :destroy
has_many :users, through: :user_questions
has_many :line_of_business_questions, dependent: :destroy
has_many :lines_of_business, through: :line_of_business_questions, :source => :line_of_business
end
class User < ApplicationRecord
has_many :user_questions, dependent: :destroy
has_many :questions, through: :user_questions
end
class LineOfBusiness < ApplicationRecord
self.table_name = "lines_of_business"
has_many :line_of_business_questions, dependent: :destroy
has_many :questions, through: :line_of_business_questions
end
def question_params
params.require(:question).permit(:name, :input_type, user_ids: [], lines_of_business_ids: [])
end
I have a self join table on my product model using a model called matches as the join table. What I would like to do is when deleting a product to have the associated product removed but not deleted. Right now I am trying dependent: :destroy which doesn't work, but I know its not what I want because I don't want to delete the self associated product.
product.rb
class Product < ApplicationRecord
...
has_many :variations, -> { order(:order) }, dependent: :destroy
has_and_belongs_to_many :categories
has_and_belongs_to_many :tags
has_many :matches
has_many :matched_products, through: :matches, dependent: :destroy
...
end
match.rb
class Match < ActiveRecord::Base
belongs_to :product
belongs_to :matched_product, class_name: 'Product', dependent: :destroy
has_many :variations, :through => :matched_product
end
I suggest you update your models as follows:
product.rb
class Product < ApplicationRecord
...
has_many :variations, -> { order(:order) }, dependent: :destroy
has_and_belongs_to_many :categories
has_and_belongs_to_many :tags
has_many :matches, dependent: :destroy
has_many :product_matches, class_name: 'Match', foreign_key: :matched_product_id, dependent: :destroy
has_many :matched_products, through: :matches
...
end
This will ensure that all matches records are deleted when deleting a product whether the product is a product or matched_product in the match record. Removing dependent: :destroy from has_many :matched_products will prevent deletion of the, well, matched_products.
match.rb
class Match < ActiveRecord::Base
belongs_to :product
belongs_to :matched_product, class_name: 'Product'
has_many :variations, :through => :matched_product
end
Similar to above, removing dependent: :destroy from belongs_to :matched_product, class_name: 'Product' will prevent deletion of the matched_product.
I have 3 tables
class Product < ApplicationRecord
has_many :accessories
has_many :product_attribute_categories, through: :accessories, dependent: :destroy
end
class Accessory < ApplicationRecord
belongs_to :product
belongs_to :product_attribute_category
end
class ProductAttributeCategory < ApplicationRecord
has_many :accessories, dependent: :destroy
has_many :products, through: :accessories
has_many :product_attributes, dependent: :destroy
now when I try to delete a ProductAttributeCategory, I want all the accessories that it is associated with to be deleted. So it wont return any errors.
However it returns Accessory is marked as readonly. So I'm wondering if what I'm doing is not optimal and what I should do instead.
With a regular has_many, there's the option of :dependent => :destroy to delete the associations when the parent record is deleted. With has_many :through, there might be other parents associated to the child records, so :dependent => :destroy doesn't have any effect.
How do you ensure child records are deleted after they are orphaned from the last HMT association?
The solution I have found seems to be an after_destroy callback, such as this:
class Parent < ActiveRecord::Base
has_many :children, :through => :parentage
after_destroy :destroy_orphaned_children
private
def destroy_orphaned_children
children.each do |child|
child.destroy if child.parents.empty?
end
end
end
On the join model, use "belongs_to :model, dependent: :destroy"
for example, if you want to destroy a patient once their doctor is destroyed, and doctor has_many patients though appointments
Class Appointment
belongs_to :doctor
belongs_to :patient, dependent: :destroy
Class Doctor
has_many :appointments, dependent: :destroy
has_many :patients, through: :appointments
Class Patient
has_many :appointments
has_many :doctors, through: :appointments