1

I have the following models:

class Article < ActiveRecord::Base
    has_many :comments, :as => :subject, :dependent => :destroy
    has_many :deleted_comments, :as => :subject, :dependent => :destroy
end

class DeletedComment < ActiveRecord::Base
    belongs_to :subject, :polymorphic => true
end

class Comment < ActiveRecord::Base
    belongs_to :subject, :polymorphic => true
    before_destroy :create_deleted_comment

    def create_deleted_comment
        DeletedComment.create!(....)
    end
end

In my database, I have quite a few DeletedComment objects where the subject is nil. The DeletedComment (and Comment) model stores :article_id, and for the ones where the subject is nil, Article.find(deleted_comment.article_id) raises an ActiveRecord::RecordNotFound error.

Are there any cases where the :dependent => :destroy would destroy the parent record but leave the dependencies untouched?

Is it possible that in some cases when I delete an Article the deleted_comments are destroyed before comments? and when the comments are destroyed, deleted_comments are created and not destroyed (because ActiveRecord has already checked the dependent deleted_comment and tried to destroy any dependencies)?

tanookiben
  • 22,575
  • 8
  • 27
  • 25

1 Answers1

1

According to official documentation:

Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order for the associations to work as expected, ensure that you store the base model for the STI models in the type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts and member posts that use the posts table for STI. In this case, there must be a type column in the posts table.

class Asset < ActiveRecord::Base
  belongs_to :attachable, polymorphic: true

  def attachable_type=(sType)
     super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

class Post < ActiveRecord::Base
  # because we store "Post" in attachable_type now dependent: :destroy will work
  has_many :assets, as: :attachable, dependent: :destroy
end

class GuestPost < Post
end

class MemberPost < Post
end

I guess you could use examle and do something like:

class Article < ActiveRecord::Base
  # for deletion only
  has_many :abstract_comments, :as => :subject, :dependent => :destroy

  # for 'manual' access/edition 
  has_many :comments,         :as => :subject
  has_many :deleted_comments, :as => :subject
end

class AbstractComment < ActiveRecord::Base
  belongs_to :subject, :polymorphic => true

  def attachable_type=(sType)
    super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

class DeletedComment < AbstractComment
end

class Comment < AbstractComment
  before_destroy :create_deleted_comment

  def create_deleted_comment
    DeletedComment.create!(....)
  end
end
Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64
  • I could be wrong, but I don't think doing a `has_many :as => X` leads to any overriding. My Comment and DeletedComment models have `subject_id` and `subject_type`, and `subject_type` is "Article," so that's how for most of those objects, doing comment.subject or deleted_comment.subject returns the appropriate Article. I tend to think of `has_many :as => X` as "treat this current model as X" instead of "this current model has a method to retrieve X via model.X" – tanookiben Dec 03 '13 at 21:53
  • Your example is really helpful and seems like a good way to go. But I'm just curious as to why my existing implementation works sometimes and not others. I can't seem to be able to reproduce this issue in the Rails console. Even my test cases for this situation doesn't seem to run into any issues. – tanookiben Dec 03 '13 at 22:36
  • Try this page to configure debug logging: http://stackoverflow.com/questions/12346192/rails-view-activerecord-sql-statements-on-debug-console When we see the difference in executed queries we might find something more about the original issue. My wild guess is that polymorphic relations were intended to be used with manual manipulation of `*_type` and the default behavior doesn't delete anything just-in-case. – Mateusz Kubuszok Dec 04 '13 at 00:43