I have a problem with one of my models. The parent object has 2 polymorphic relationships both specifying :dependent => :destroy. This all works fine until you attempt to delete the parent and the parent''s before_destroy callback returns false. When the parent''s before_destroy callback returns false, the parent is not deleted, but the transaction is still committed so all of the objects from the polymorphic relationships are destroyed. This is obviously not desirable. I have verified that the call back is returning false. Is there something I am missing or is this just normal. I can remove the :dependent => :destroy and place the relevant code in my before_destroy callback, but this seems to defeat the purpose of :dependent and would get really messy on objects with many dependencies. Here is the abbreviated code in question: class Location < AbstractBase <snip> has_one :address, :as => :addressed, :dependent => :destroy has_one :contact, :as => :contactable, :dependent => :destroy <snip> before_destroy :any_dependent_assets? <snip> end class AbstractBase < ActiveRecord::Base <snip> ########### protected ########### #this is used as a filter to stop deletion if there are any dependent assets def any_dependent_assets? if self.respond_to? :assets c = self.assets.count if c > 0 self.errors.add self.human_name, "is still assigned to #{c} assets" logger.debug "we are returning false" return false end end logger.debug "we are returning true" true end end Here is the log snippet from the transaction in question: SQL (0.160060) BEGIN Address Columns (0.158866) SHOW FIELDS FROM addresses Address Load (0.163060) SELECT * FROM addresses WHERE (addresses.addressed_id = 1 AND addresses.addressed_type = ''Location'') LIMIT 1 Address Destroy (0.159641) WHERE `id` = 266 Contact Columns (0.035453) SHOW FIELDS FROM contacts Contact Load (0.036147) SELECT * FROM contacts WHERE (contacts.contactable_id = 1 AND contacts.contactable_type = ''Location'') LIMIT 1 Contact Destroy (0.031610) WHERE `id` = 224 Asset Columns (0.036145) SHOW FIELDS FROM assets SQL (0.029946) SELECT count(*) AS count_all FROM assets WHERE (assets.deleted_at IS NULL OR assets.deleted_at > ''2008-01-07 23:59:05'') AND (assets.location_id = 1) we are returning false SQL (0.355231) COMMIT -- Sincerely, William Pratt --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
You can run this in a transaction block: @parent = Parent.find(params[:id]) Parent.transaction do @parent.destroy end if @parent.destroy raises any exception the entire transaction will be rolled back and none of the children will be deleted. On Jan 8, 2008 10:48 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org> wrote:> I have a problem with one of my models. The parent object has 2 > polymorphic relationships both specifying :dependent => :destroy. This all > works fine until you attempt to delete the parent and the parent''s > before_destroy callback returns false. When the parent''s before_destroy > callback returns false, the parent is not deleted, but the transaction is > still committed so all of the objects from the polymorphic relationships are > destroyed. This is obviously not desirable. I have verified that the call > back is returning false. Is there something I am missing or is this just > normal. I can remove the :dependent => :destroy and place the relevant code > in my before_destroy callback, but this seems to defeat the purpose of > :dependent and would get really messy on objects with many dependencies. > > Here is the abbreviated code in question: > > class Location < AbstractBase > <snip> > has_one :address, :as => :addressed, :dependent => :destroy > has_one :contact, :as => :contactable, :dependent => :destroy > <snip> > before_destroy :any_dependent_assets? > <snip> > end > > class AbstractBase < ActiveRecord::Base > <snip> > ########### > protected > ########### > > #this is used as a filter to stop deletion if there are any dependent > assets > def any_dependent_assets? > if self.respond_to? :assets > c = self.assets.count > if c > 0 > self.errors.add self.human_name, "is still assigned to #{c} > assets" > logger.debug "we are returning false" > return false > end > end > logger.debug "we are returning true" > true > end > end > > Here is the log snippet from the transaction in question: > > SQL (0.160060) BEGIN > Address Columns (0.158866) SHOW FIELDS FROM addresses > Address Load (0.163060) SELECT * FROM addresses WHERE ( > addresses.addressed_id = 1 AND addresses.addressed_type = ''Location'') > LIMIT 1 > Address Destroy (0.159641) > WHERE `id` = 266 > > Contact Columns (0.035453) SHOW FIELDS FROM contacts > Contact Load (0.036147) SELECT * FROM contacts WHERE ( > contacts.contactable_id = 1 AND contacts.contactable_type = ''Location'') > LIMIT 1 > Contact Destroy (0.031610) > WHERE `id` = 224 > > Asset Columns (0.036145) SHOW FIELDS FROM assets > SQL (0.029946) SELECT count(*) AS count_all FROM assets WHERE ( > assets.deleted_at IS NULL OR assets.deleted_at > ''2008-01-07 23:59:05'') > AND (assets.location_id = 1) > we are returning false > SQL (0.355231) COMMIT > > -- > Sincerely, > > William Pratt > > > > >-- Ryan Bigg http://www.frozenplague.net Feel free to add me to MSN and/or GTalk as this email. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Hey Ryan, thank for the suggestion but that won''t help as the destroy is already wrapped in a transaction and the children are still deleted. I can get things to work if I raise an exception in the parent''s before filter and catch it in a begin / rescue / end block in the controller. I guess what I really want to know is why would :dependent children be destroyed if the parent''s before_destroy filter returns false? Seems to me that is something is "dependent" than it should be just that. It would be deleted only if the parent can be. I was expecting to see the transaction rolled back when the parent''s before_destroy callback returned false -Bill Ryan Bigg wrote:> You can run this in a transaction block: > @parent = Parent.find(params[:id]) > Parent.transaction do > @parent.destroy > end > > if @parent.destroy raises any exception the entire transaction will be > rolled back and none of the children will be deleted. > > > On Jan 8, 2008 10:48 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org > <mailto:billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org>> wrote: > > I have a problem with one of my models. The parent object has 2 > polymorphic relationships both specifying :dependent => :destroy. > This all works fine until you attempt to delete the parent and the > parent''s before_destroy callback returns false. When the parent''s > before_destroy callback returns false, the parent is not deleted, > but the transaction is still committed so all of the objects from > the polymorphic relationships are destroyed. This is obviously not > desirable. I have verified that the call back is returning false. > Is there something I am missing or is this just normal. I can > remove the :dependent => :destroy and place the relevant code in > my before_destroy callback, but this seems to defeat the purpose > of :dependent and would get really messy on objects with many > dependencies. > > Here is the abbreviated code in question: > > class Location < AbstractBase > <snip> > has_one :address, :as => :addressed, :dependent => :destroy > has_one :contact, :as => :contactable, :dependent => :destroy > <snip> > before_destroy :any_dependent_assets? > <snip> > end > > class AbstractBase < ActiveRecord::Base > <snip> > ########### > protected > ########### > > #this is used as a filter to stop deletion if there are any > dependent assets > def any_dependent_assets? > if self.respond_to? :assets > c = self.assets.count > if c > 0 > self.errors.add self.human_name, "is still assigned to > #{c} assets" > logger.debug "we are returning false" > return false > end > end > logger.debug "we are returning true" > true > end > end > > Here is the log snippet from the transaction in question: > > SQL (0.160060) BEGIN > Address Columns (0.158866) SHOW FIELDS FROM addresses > Address Load (0.163060) SELECT * FROM addresses WHERE > (addresses.addressed_id = 1 AND addresses.addressed_type > ''Location'') LIMIT 1 > Address Destroy (0.159641) > WHERE `id` = 266 > > Contact Columns (0.035453) SHOW FIELDS FROM contacts > Contact Load (0.036147) SELECT * FROM contacts WHERE > (contacts.contactable_id = 1 AND contacts.contactable_type > ''Location'') LIMIT 1 > Contact Destroy (0.031610) > WHERE `id` = 224 > > Asset Columns (0.036145) SHOW FIELDS FROM assets > SQL (0.029946) SELECT count(*) AS count_all FROM assets WHERE > (assets.deleted_at IS NULL OR assets.deleted_at > ''2008-01-07 > 23:59:05'') AND (assets.location_id = 1) > we are returning false > SQL (0.355231) COMMIT > > -- > Sincerely, > > William Pratt > > > > > > > > > -- > Ryan Bigg > http://www.frozenplague.net > Feel free to add me to MSN and/or GTalk as this email. > >-- Sincerely, William Pratt --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I''m guessing you''re a little bit confused on precisely how the dependent destroy works, along with a before_destroy. 1. You tell Rails to delete the parent record. 2. It checks for it''s associated records and their :dependent settings. 3. If it is dependent => :destroy then it will delete these records. 4. Processes before_destroy on parent record. 5. Parent record is destroyed if before_destroy returns true. The transaction will not be cancelled if the before_destroy returns false. Get it to raise an exception and it should roll it back. On Jan 8, 2008 11:18 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org> wrote:> Hey Ryan, thank for the suggestion but that won''t help as the destroy is > already wrapped in a transaction and the children are still deleted. I can > get things to work if I raise an exception in the parent''s before filter and > catch it in a begin / rescue / end block in the controller. I guess what I > really want to know is why would :dependent children be destroyed if the > parent''s before_destroy filter returns false? Seems to me that is something > is "dependent" than it should be just that. It would be deleted only if the > parent can be. I was expecting to see the transaction rolled back when the > parent''s before_destroy callback returned false > > -Bill > > Ryan Bigg wrote: > > You can run this in a transaction block: > @parent = Parent.find(params[:id]) > Parent.transaction do > @parent.destroy > end > > if @parent.destroy raises any exception the entire transaction will be > rolled back and none of the children will be deleted. > > > On Jan 8, 2008 10:48 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org> wrote: > > > I have a problem with one of my models. The parent object has 2 > > polymorphic relationships both specifying :dependent => :destroy. This all > > works fine until you attempt to delete the parent and the parent''s > > before_destroy callback returns false. When the parent''s before_destroy > > callback returns false, the parent is not deleted, but the transaction is > > still committed so all of the objects from the polymorphic relationships are > > destroyed. This is obviously not desirable. I have verified that the call > > back is returning false. Is there something I am missing or is this just > > normal. I can remove the :dependent => :destroy and place the relevant code > > in my before_destroy callback, but this seems to defeat the purpose of > > :dependent and would get really messy on objects with many dependencies. > > > > Here is the abbreviated code in question: > > > > class Location < AbstractBase > > <snip> > > has_one :address, :as => :addressed, :dependent => :destroy > > has_one :contact, :as => :contactable, :dependent => :destroy > > <snip> > > before_destroy :any_dependent_assets? > > <snip> > > end > > > > class AbstractBase < ActiveRecord::Base > > <snip> > > ########### > > protected > > ########### > > > > #this is used as a filter to stop deletion if there are any dependent > > assets > > def any_dependent_assets? > > if self.respond_to? :assets > > c = self.assets.count > > if c > 0 > > self.errors.add self.human_name, "is still assigned to #{c} > > assets" > > logger.debug "we are returning false" > > return false > > end > > end > > logger.debug "we are returning true" > > true > > end > > end > > > > Here is the log snippet from the transaction in question: > > > > SQL (0.160060) BEGIN > > Address Columns (0.158866) SHOW FIELDS FROM addresses > > Address Load (0.163060) SELECT * FROM addresses WHERE ( > > addresses.addressed_id = 1 AND addresses.addressed_type = ''Location'') > > LIMIT 1 > > Address Destroy (0.159641) > > WHERE `id` = 266 > > > > Contact Columns (0.035453) SHOW FIELDS FROM contacts > > Contact Load (0.036147) SELECT * FROM contacts WHERE ( > > contacts.contactable_id = 1 AND contacts.contactable_type = ''Location'') > > LIMIT 1 > > Contact Destroy (0.031610) > > WHERE `id` = 224 > > > > Asset Columns (0.036145) SHOW FIELDS FROM assets > > SQL (0.029946) SELECT count(*) AS count_all FROM assets WHERE ( > > assets.deleted_at IS NULL OR assets.deleted_at > ''2008-01-07 23:59:05'') > > AND (assets.location_id = 1) > > we are returning false > > SQL (0.355231) COMMIT > > > > -- > > Sincerely, > > > > William Pratt > > > > > > > > > > > > > > > -- > Ryan Bigg > http://www.frozenplague.net > Feel free to add me to MSN and/or GTalk as this email. > > > -- > Sincerely, > > William Pratt > > > > >-- Ryan Bigg http://www.frozenplague.net Feel free to add me to MSN and/or GTalk as this email. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Ok, thats what I ended up with. Thanks again. Ryan Bigg wrote:> I''m guessing you''re a little bit confused on precisely how the > dependent destroy works, along with a before_destroy. > > 1. You tell Rails to delete the parent record. > 2. It checks for it''s associated records and their :dependent settings. > 3. If it is dependent => :destroy then it will delete these records. > 4. Processes before_destroy on parent record. > 5. Parent record is destroyed if before_destroy returns true. > > The transaction will not be cancelled if the before_destroy returns > false. Get it to raise an exception and it should roll it back. > > On Jan 8, 2008 11:18 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org > <mailto:billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org>> wrote: > > Hey Ryan, thank for the suggestion but that won''t help as the > destroy is already wrapped in a transaction and the children are > still deleted. I can get things to work if I raise an exception in > the parent''s before filter and catch it in a begin / rescue / end > block in the controller. I guess what I really want to know is why > would :dependent children be destroyed if the parent''s > before_destroy filter returns false? Seems to me that is something > is "dependent" than it should be just that. It would be deleted > only if the parent can be. I was expecting to see the transaction > rolled back when the parent''s before_destroy callback returned false > > -Bill > > Ryan Bigg wrote: >> You can run this in a transaction block: >> @parent = Parent.find(params[:id]) >> Parent.transaction do >> @parent.destroy >> end >> >> if @parent.destroy raises any exception the entire transaction >> will be rolled back and none of the children will be deleted. >> >> >> On Jan 8, 2008 10:48 AM, William Pratt <billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org >> <mailto:billp-YbheRAKfYF4eIZ0/mPfg9Q@public.gmane.org>> wrote: >> >> I have a problem with one of my models. The parent object has >> 2 polymorphic relationships both specifying :dependent => >> :destroy. This all works fine until you attempt to delete the >> parent and the parent''s before_destroy callback returns >> false. When the parent''s before_destroy callback returns >> false, the parent is not deleted, but the transaction is >> still committed so all of the objects from the polymorphic >> relationships are destroyed. This is obviously not desirable. >> I have verified that the call back is returning false. Is >> there something I am missing or is this just normal. I can >> remove the :dependent => :destroy and place the relevant code >> in my before_destroy callback, but this seems to defeat the >> purpose of :dependent and would get really messy on objects >> with many dependencies. >> >> Here is the abbreviated code in question: >> >> class Location < AbstractBase >> <snip> >> has_one :address, :as => :addressed, :dependent => :destroy >> has_one :contact, :as => :contactable, :dependent => :destroy >> <snip> >> before_destroy :any_dependent_assets? >> <snip> >> end >> >> class AbstractBase < ActiveRecord::Base >> <snip> >> ########### >> protected >> ########### >> >> #this is used as a filter to stop deletion if there are any >> dependent assets >> def any_dependent_assets? >> if self.respond_to? :assets >> c = self.assets.count >> if c > 0 >> self.errors.add self.human_name, "is still assigned >> to #{c} assets" >> logger.debug "we are returning false" >> return false >> end >> end >> logger.debug "we are returning true" >> true >> end >> end >> >> Here is the log snippet from the transaction in question: >> >> SQL (0.160060) BEGIN >> Address Columns (0.158866) SHOW FIELDS FROM addresses >> Address Load (0.163060) SELECT * FROM addresses WHERE >> (addresses.addressed_id = 1 AND addresses.addressed_type >> ''Location'') LIMIT 1 >> Address Destroy (0.159641) >> WHERE `id` = 266 >> >> Contact Columns (0.035453) SHOW FIELDS FROM contacts >> Contact Load (0.036147) SELECT * FROM contacts WHERE >> (contacts.contactable_id = 1 AND contacts.contactable_type >> ''Location'') LIMIT 1 >> Contact Destroy (0.031610) >> WHERE `id` = 224 >> >> Asset Columns (0.036145) SHOW FIELDS FROM assets >> SQL (0.029946) SELECT count(*) AS count_all FROM assets >> WHERE (assets.deleted_at IS NULL OR assets.deleted_at > >> ''2008-01-07 23:59:05'') AND (assets.location_id = 1) >> we are returning false >> SQL (0.355231) COMMIT >> >> -- >> Sincerely, >> >> William Pratt >> >> >> >> >> >> >> >> >> -- >> Ryan Bigg >> http://www.frozenplague.net >> Feel free to add me to MSN and/or GTalk as this email. >> > > -- > Sincerely, > > William Pratt > > > > > > > > > -- > Ryan Bigg > http://www.frozenplague.net > Feel free to add me to MSN and/or GTalk as this email. > >-- Sincerely, William Pratt --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---