Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #10592 (closed enhancement: fixed)

Opened 1 year ago

Last modified 11 months ago

[PATCH] belongs_to should take notice of the :dependent option

Reported by: jonathan_viney Assigned to: core
Priority: normal Milestone: 2.1
Component: ActiveRecord Version: edge
Severity: normal Keywords: belongs_to dependent
Cc:

Description

The belongs_to association currently ignores any :dependent option that is passed to it. This patch allows for :destroy and :delete to be specified and adds tests and docs.

Attachments

belongs_to_dependent.patch (5.8 kB) - added by jonathan_viney on 12/22/07 02:08:29.
belongs_to_dependent_2.patch (6.2 kB) - added by jonathan_viney on 12/22/07 11:50:17.
belongs_to_dependent_3.patch (6.7 kB) - added by jonathan_viney on 01/19/08 04:27:12.
author_addresses.diff (345 bytes) - added by DefV on 01/21/08 12:03:08.

Change History

12/22/07 02:08:29 changed by jonathan_viney

  • attachment belongs_to_dependent.patch added.

12/22/07 04:00:08 changed by matt

-1

I don't think that's a very good idea, a belongs_to relationship has a has_many relationship on the other end. I don't see why one would want to destroy the object it belong to.

I might have missed your point though.

(follow-up: ↓ 3 ) 12/22/07 04:03:04 changed by jonathan_viney

The :dependent option should not be used with a has_many on the other end. My use case is the following:

class Person < ActiveRecord::Base
  belongs_to :address, :dependent => :destroy
end

class Address < ActiveRecord::Base
end

It seems perfectly reasonable to me.

(in reply to: ↑ 2 ; follow-up: ↓ 4 ) 12/22/07 04:22:52 changed by matt

  • status changed from new to closed.
  • resolution set to invalid.

Replying to jonathan_viney:

The :dependent option should not be used with a has_many on the other end. My use case is the following: {{{ class Person < ActiveRecord::Base belongs_to :address, :dependent => :destroy end class Address < ActiveRecord::Base end }}} It seems perfectly reasonable to me.

What if you had something like that:

class Person < ActiveRecord::Base
  belongs_to :address, :dependent => :destroy
end

class Address < ActiveRecord::Base
  has_many :people
end

Many people would live at the same address, if I now delete one person, the whole address gets destroyed? That doesn't make sense to me.

I think you should be using the has_one relationship and by doing so, you'd get the :dependent => :destroy and :dependent => :nullify options you're after.

Unless you have a compelling case against the has_one, I think this modification isn't valid.

(in reply to: ↑ 3 ) 12/22/07 04:34:58 changed by jonathan_viney

  • status changed from closed to reopened.
  • resolution deleted.

Replying to matt:

What if you had something like that: {{{ class Person < ActiveRecord::Base belongs_to :address, :dependent => :destroy end class Address < ActiveRecord::Base has_many :people end }}} Many people would live at the same address, if I now delete one person, the whole address gets destroyed? That doesn't make sense to me.

But why would you specify :dependent there? You're specifying behaviour you don't want and then then complaining when it happens!

I think you should be using the has_one relationship and by doing so, you'd get the :dependent => :destroy and :dependent => :nullify options you're after. Unless you have a compelling case against the has_one, I think this modification isn't valid.

I have some models that have two addresses, and I think I'm right in saying that you can not have two has_ones to the same table. Eg:

class Person < ActiveRecord::Base
  belongs_to :street_address, :class_name => "Address", :dependent => :destroy
  belongs_to :mailing_address, :class_name => "Address", :dependent => :destroy
end

class Address < ActiveRecord::Base
end

How does that translate nicely into using has_one instead of belongs_to?

Can we get some other people's input on this?

12/22/07 04:43:14 changed by bitsweat

  • keywords set to belongs_to dependent.
  • milestone changed from 2.x to 2.1.

Looks good to me.

@matt, if you had set things up this way, you could have a foreign key constraint restricting its deletion.

(follow-up: ↓ 7 ) 12/22/07 04:49:34 changed by matt

my bad, I didn't think about the second case.

However, I still think it's a bit dangerous to go this way, not that we shouldn't do it but maybe have an extra warning in the doc or something.

Sorry Jonathan for the misunderstanding.

(in reply to: ↑ 6 ) 12/22/07 05:22:18 changed by jonathan_viney

All good. I think a warning in the docs is a good idea.

Replying to matt:

my bad, I didn't think about the second case. However, I still think it's a bit dangerous to go this way, not that we shouldn't do it but maybe have an extra warning in the doc or something. Sorry Jonathan for the misunderstanding.

12/22/07 11:50:17 changed by jonathan_viney

  • attachment belongs_to_dependent_2.patch added.

12/22/07 11:51:10 changed by jonathan_viney

Added patch with docs mentioning that dependent should not be used with belongs_to/has_many.

(follow-up: ↓ 10 ) 01/03/08 22:07:11 changed by manitoba98

I'd agree that belongs_to :dependent would be a dangerous idea.

With regard to jonathan_viney, I'd suggest the following:

class Person < ActiveRecord::Base
  with_options :class_name => "Address", :dependent => :destroy do |addresses|
    addresses.has_one :street_address, :conditions => {:type => "street"}
    addresses.has_one :mailing_address, :conditions => {:type => "mailing}
  end
end

class Address < ActiveRecord::Base
  belongs_to :person
  validates_inclusion_of :type, :in => %w(street mailing)
end

That's untested, and there may be a few gotchas (create_street_address probably wouldn't automatically assign type, for instance), but it would achieve what you want, and I feel that :dependent really only belongs on has_one and has_many. Perhaps this could be implemented as a shortcut (some metaprogramming), but it seems like such an edge case that even that may not be worth the effort.

(in reply to: ↑ 9 ) 01/07/08 01:49:45 changed by jonathan_viney

Any option can be used in the wrong place. Placing a :dependent option on any relationship that should not be dependent will result in incorrect behaviour, that's just common sense. If you ask for behaviour you do not want, you shouldn't be surprised when you get it! I can see that some people may incorrectly use it with a belongs_to/has_many scenario, so the documentation mentions that it should not be used in this case.

The natural solution for my situation is belongs_to :dependent. I'm sure there are many ways which I could hack in the required behaviour, but that does not seem like a good solution.

Replying to manitoba98:

I'd agree that belongs_to :dependent would be a dangerous idea. With regard to jonathan_viney, I'd suggest the following:

01/12/08 08:14:53 changed by railsjitsu

I have to say I like this idea. :dependent => :destroy denotes that it is a somewhat dangerous method, if you don't want your child referenced rows to be wacked don't set this value. At least that is my thinking. I have needed this a few times to keep orphan rows from sticking around.

+1

01/19/08 03:27:02 changed by bitsweat

could you update the patch for trunk?

01/19/08 04:27:12 changed by jonathan_viney

  • attachment belongs_to_dependent_3.patch added.

01/19/08 04:27:47 changed by jonathan_viney

Sure thing.

01/19/08 05:30:45 changed by bitsweat

  • status changed from reopened to closed.
  • resolution set to fixed.

(In [8675]) belongs_to supports :dependent => :destroy and :delete. Closes #10592.

01/21/08 11:58:55 changed by DefV

  • status changed from closed to reopened.
  • resolution deleted.

changeset 8675 doesn't include the author_addresses.yml file. This makes the test fail. I attached a new patch that creates the table, which makes the test succeed.

01/21/08 12:03:08 changed by DefV

  • attachment author_addresses.diff added.

01/21/08 17:21:16 changed by bitsweat

  • status changed from reopened to closed.
  • resolution set to fixed.

(In [8682]) Add missing author_addresses.yml fixture. Closes #10592.