Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define allow_destroy and :dependent => :destroy in Rails?

Given the following database model, how and where would you define the deletion relationships between the models? I figured out the basic table association setup but when I want to add dependencies to enable the deletion of nested objects I get lost.

Database relationship model

Here is the relationship model I created.

class User < ActiveRecord::Base
  has_many :studies
end

class Study < ActiveRecord::Base
  has_many :internships
  belongs_to :student, :class_name => "User", :foreign_key => "user_id"
  belongs_to :subject
  belongs_to :university, :class_name => "Facility", :foreign_key => "facility_id"
  accepts_nested_attributes_for :subject, :university, :locations
end

class Subject < ActiveRecord::Base
  has_many :studies
end

class Internship < ActiveRecord::Base
  belongs_to :study
  belongs_to :company, :class_name => "Facility", :foreign_key => 'facility_id'
  accepts_nested_attributes_for :company, :study
end

class Facility < ActiveRecord::Base
  has_many :internships
  has_many :locations
  has_many :studies
  accepts_nested_attributes_for :locations
end

class Location < ActiveRecord::Base
  belongs_to :facility
end

Where would you put :dependent => :destroy and :allow_destroy => true to enable the following scenarios? I do not want to confuse you. Therefore, I leave out my tryings.

Internship scenario: A user wants to delete an internship.

  • Its associated company (facility) can be deleted if the company is not related to another internship.
  • If so, the locations of the associated company can be deleted.
  • The related study will not be affected.

Study scenario: A user wants to delete a study.

  • Its associated subject can be deleted if no other study refers to this subject.
  • Its associated university (facility) can be deleted if no other study refers to this university.
  • Its associated internships can be deleted. The company can only be deleted if no other internship refers to it.

I am totally unsure whether I can add :dependent => :destroy only after has_one and has_many or also after belongs_to.


Edit: To simplify the problem please stick to the following (reduced) example implementation.

class Study < ActiveRecord::Base
  belongs_to :subject
  accepts_nested_attributes_for :subject, :allow_destroy => true
end

class Subject < ActiveRecord::Base
  has_many :studies, :dependent => :destroy
end

In my view I provide the following link.

<%= link_to "Destroy", study, :method => :delete, :confirm => "Are you sure?" %>

The path is based on the named routes given by a restful configuration in routes.rb.

resources :studies
resources :subjects

The study will be deleted when I click the link - the subjects stays untouched. Why?

like image 964
JJD Avatar asked Mar 23 '11 13:03

JJD


2 Answers

I think your relations are the wrong way around here...
The accepts_nested_attributes_for should be declared on the model that has_many for the model that it has_many of. Also, in your example, destroying the subject would enforce dependent_destroy on the many studies, not the other way around.

like image 64
james Avatar answered Oct 19 '22 23:10

james


You can add :dependent => :destroy to all three but I'm not sure if that'll give you enough power to do the checks required before determining whether an associated object should be destroyed.

You have a few options.

Add a before_destroy callback on each model that raises an exception or stops the delete from occurring.

class Facility < ActiveRecord::Base
  has_many :internships
  has_many :locations
  has_many :studies

  def before_destroy
    raise SomethingException if internships.any? || ...
    # or
    errors.add(...
  end
end

or do it silently by overriding destroy

class Facility < ActiveRecord::Base
  has_many :internships
  has_many :locations
  has_many :studies

  def destroy
    return false if internships.any || ...
    super
  end
end

Note: this is basically meant for guidance only and may not be the correct way of overriding destroy etc...

like image 29
lebreeze Avatar answered Oct 20 '22 01:10

lebreeze