Friday, April 28, 2006

Associations aren't :dependent => true anymore

Posted by marcel

Up until the 1.1 release, the way to automatically destroy has_many associations when the owner was itself destroyed was to either use the :dependent or :exclusively_dependent option when declaring the has_many.


class Account < ActiveRecord::Base
  has_many :members, :dependent => true
end

or


class Brand < ActiveRecord::Base
  has_many :products, :exclusively_dependent => true
end

The :dependent option instantiated all the associated objects and called destroy on each one. Destroy in turn triggers the callbacks defined on that associated model, such as those declared with before_destroy and after_destroy.

The :exclusively_dependent option, on the other hand, did not instantiate all the associated objects. Rather, it just generated a single SQL statement which deleted the associated records without first creating objects for each one. This buys you efficiency when you have no need for the flexibility of triggering model callbacks.

Since 1.1, the API for garbage collecting associated records has been consolidated into the :dependent option. Rather than saying :dependent => true, you now pass one of several symbols to the :dependent option which describes how the association is dependent on the owner.

Declaring the has many as :dependent => :destroy is the same as what used to be declared as :dependent => true. When the owner is destroyed, all the associated records are instantiated and destroyed.


class Account < ActiveRecord::Base
  # Deprecated
  # has_many :members, :dependent => true
  
  # In favor of
  has_many :members, :dependent => :destroy
end

The new way to achieve the now deprecated :exclusively_dependent configuration is to use :dependent => :delete_all rather than :dependent => :destroy.


class Brand < ActiveRecord::Base
  # Deprecated
  # has_many :products, :exclusively_dependent => true

  # In favor of
  has_many :products, :dependent => :delete_all
end

The :destroy and :delete_all option symbols are so named because they correspond with the behavior achieved by calling destroy versus delete on a model object. One triggers callbacks, the other just generates the delete SQL statement.

As an aside, another valid option is :dependent => :nullify which is similar to :dependent => :delete_all except rather than deleting the associated records, it just sets their foreign keys to NULL. This effectively removes the association, without removing the associated records from the database table.

As always, the semantics of :dependent => :destroy and :dependent => :delete_all are mutually exclusive, which this new API makes a bit more apparent.

It should be noted that declaring dependencies is not required when setting up has many associations. It is simply an option for when you desire such functionality.

Keep in mind that for now :dependent => true and :exclusively_dependent => true will still be supported, but they have been marked as deprecated and could be taken out in the future.