From 4f7bdc8f74d48300a6dc13413a43e65dca5c8384 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 7 Feb 2011 00:00:15 +0000 Subject: Documentation for recent refinements to association deletion --- activerecord/lib/active_record/associations.rb | 78 +++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b90838a52b..a2db592c7d 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -232,10 +232,9 @@ module ActiveRecord # others.empty? | X | X | X # others.clear | X | X | X # others.delete(other,other,...) | X | X | X - # others.delete_all | X | X | + # others.delete_all | X | X | X # others.destroy_all | X | X | X # others.find(*args) | X | X | X - # others.find_first | X | | # others.exists? | X | X | X # others.uniq | X | X | X # others.reset | X | X | X @@ -833,6 +832,73 @@ module ActiveRecord # * does not work with :polymorphic associations. # * for +belongs_to+ associations +has_many+ inverse associations are ignored. # + # == Deleting from associations + # + # === Dependent associations + # + # +has_many+, +has_one+ and +belongs_to+ associations support the :dependent option. + # This allows you to specify that associated records should be deleted when the owner is + # deleted. + # + # For example: + # + # class Author + # has_many :posts, :dependent => :destroy + # end + # Author.find(1).destroy # => Will destroy all of the author's posts, too + # + # The :dependent option can have different values which specify how the deletion + # is done. For more information, see the documentation for this option on the different + # specific association types. + # + # === Delete or destroy? + # + # +has_many+ and +has_and_belongs_to_many+ associations have the methods destroy, + # delete, destroy_all and delete_all. + # + # For +has_and_belongs_to_many+, delete and destroy are the same: they + # cause the records in the join table to be removed. + # + # For +has_many+, destroy will always call the destroy method of the + # record(s) being removed so that callbacks are run. However delete will either + # do the deletion according to the strategy specified by the :dependent option, or + # if no :dependent option is given, then it will follow the default strategy. + # The default strategy is :nullify (set the foreign keys to nil), except for + # +has_many+ :through, where the default strategy is delete_all (delete + # the join records, without running their callbacks). + # + # There is also a clear method which is the same as delete_all, except that + # it returns the association rather than the records which have been deleted. + # + # === 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). + # # == Type safety with ActiveRecord::AssociationTypeMismatch # # If you attempt to assign an object to an association that doesn't match the inferred @@ -857,6 +923,10 @@ module ActiveRecord # 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. # [collection=objects] # Replaces the collections content by deleting and adding objects as appropriate. If the :through # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is @@ -940,7 +1010,9 @@ module ActiveRecord # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to # :restrict this object cannot be deleted if it has any associated object. # - # *Warning:* This option is ignored when used with :through option. + # If using with the :through option, the association on the join model must be + # a +belongs_to+, and the records which get deleted are the join records, rather than + # the associated records. # # [:finder_sql] # Specify a complete SQL statement to fetch the association. This is a good way to go for complex -- cgit v1.2.3