Question

I have three activerecord classes: Klass, Reservation and Certificate A Klass can have many reservations, and each reservation may have one Certificate

The definitions are as follows...

class Klass < ActiveRecord::Base
  has_many :reservations, dependent: :destroy, :autosave => true  
  has_many :certificates, through: :reservations
  attr_accessible :name

  def kill_certs
    begin
      p "In Kill_certs"
      self.certificates.destroy_all
      p "After Destroy"
    rescue Exception => e
      p "In RESCUE!"
      p e.message
    end  
  end

end

class Reservation < ActiveRecord::Base
  belongs_to :klass
  has_one :certificate, dependent: :destroy, autosave: true
  attr_accessible :klass_id, :name
end

class Certificate < ActiveRecord::Base
  belongs_to :reservation
  attr_accessible :name
end

I would like to be able to delete/destroy all the certificates for a particular klass within the klass controller with a call to Klass#kill_certs (above)

However, I get an exception with the message:

"In RESCUE!"
"Cannot modify association 'Klass#certificates' because the source 
reflection class 'Certificate' is associated to 'Reservation' via :has_one."

I('ve also tried changing the reservation class to "has_many :certificates", and then the error is...

"In RESCUE!"
"Cannot modify association 'Klass#certificates' because the source reflection
 class 'Certificate' is associated to 'Reservation' via :has_many."

It's strange that I can do Klass.first.certificates from the console and the certs from the first class are retrieved, but I can't do Klass.first.certificates.delete_all with out creating an error. Am I missing something?

Is the only way to do this..

Klass.first.reservations.each do |res|
  res.certificate.destroy
end

Thanks for any help.

Was it helpful?

Solution

RoR docs have clear explanation for this (read bold only for TLDR):

Deleting from associations

What gets deleted?

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.

This makes sense if you think about it: if you were to call post.tags.delete(Tag.find_by(name: 'food')) you would want the ‘food’ tag to be unlinked from the post, rather than for the tag itself to be removed from the database.

However, there are examples where this strategy doesn’t make sense. For example, suppose a person has many projects, and each project has many tasks. If we deleted one of a person’s tasks, we would probably not want the project to be deleted. In this scenario, the delete method won’t actually work: it can only be used if the association on the join model is a belongs_to. In other situations you are expected to perform operations directly on either the associated records or the :through association.

With a regular has_many there is no distinction between the “associated records” and the “link”, so there is only one choice for what gets deleted.

With has_and_belongs_to_many and has_many :through, if you want to delete the associated records themselves, you can always do something along the lines of person.tasks.each(&:destroy).

So you can do this:

self.certificates.each(&:destroy)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top