rubysherpas / paranoia

acts_as_paranoid for Rails 5, 6 and 7
Other
2.89k stars 529 forks source link

Support for with_deleted option on associations #176

Open noctivityinc opened 10 years ago

noctivityinc commented 10 years ago

Is there anyone to add support for with_deleted for associations the same was acts_as_paranoid does so that we can query an associated model and return everything including what was deleted?

radar commented 10 years ago

That's... already supported? I thought? post.comments.with_deleted?

noctivityinc commented 10 years ago

Sorry for not being clear. I mean in the belongs_to or has_many declarations.

NicoBleh commented 9 years ago

belongs_to :something, with_deleted: true

Would be really helpful when you have like:

delegate :stuff, to: :something

an something is soft_deleted

SebastianVomMeer commented 9 years ago

Rails 4 (not sure about 3) allows custom scopes for associations:

belongs_to :whatever, -> { with_deleted }

Therefore support should be fine there.

yonahforst commented 9 years ago

using with_deleted in the association scope doesn't work, but I think it's a rails bug: https://github.com/rails/rails/issues/13775

SebastianVomMeer commented 9 years ago

We use the scope approach with Rails 4.1.6 and haven't had any problems yet.

yonahforst commented 9 years ago

@SebastianG86 does it also work with joins and includes? i.e.

   class Chapter < ActiveRecord::Base
     belongs_to :book, -> { with_deleted}
   end

   Chapter.joins(:book)

in my case (also rails 4.1.6) I can call chapter.book and will properly unscope :deleted_at, but when I call Chapter.joins(:book) it won't return chapters from deleted books

dquimper commented 9 years ago

Anyone has a workaround for this?

NicoBleh commented 9 years ago

My approach is to unscope the :deleted_at in the query chain like:

belongs_to :messages_with_deleted, -> { unscope(where: :deleted_at) }, class_name: 'Message', foreign_key: :message_id
dquimper commented 9 years ago

Thanks for responding on Christmas Day!

Unfortunately, your solutions doesn't work for me. Maybe because my project is still on Rails 4.0.x. But you helped me understand something that I didn't know and that redirected me to renewed Google searches.

I ended up using the abstractcoder solution presented here: http://stackoverflow.com/questions/19291181/rails-4-activerecord-includesmodel-without-its-default-scope

It's not pretty, but until the Rails team fixes https://github.com/rails/rails/issues/13775, it will have to do.

Thanks and Merry Christmas to you!.

nornagon commented 9 years ago

This is still broken for me in rails 4.2, but this workaround worked for me:

has_many :foos
def foos
  super.with_deleted
end
nimzco commented 9 years ago

My app uses Rails 4.1.8 and neither @NicoBleh or @nornagon solutions work for me... Any update on the situation?

I ended doing...:

def course
  Course.with_deleted.find(course_id)
end

EDIT

This is a bad idea, because if you do your_instance.course = Course.new, your_instance.course will fail because course is not persisted...

nimzco commented 9 years ago

Here is a solution:

  def course
    Course.unscoped{ super }
  end
mamantoha commented 9 years ago

@NicoBleh thanks!

akaspick commented 8 years ago

Using the example in the README, eager loading the association doesn't work if I reference the association in a where clause.

class Person < ActiveRecord::Base
  belongs_to :group, -> { with_deleted }
end

Person.includes(:group).all # works
Person.includes(:group).where(group: {id: 5}).all # doesn't work

If I reference the association in the where clause the resulting eager loading SQL adds the "deleted_at is NULL" clause to my query, which I DO NOT want.

LEFT OUTER JOIN "groups" ON "groups"."id" = "people"."group_id" AND "groups"."deleted_at" IS NULL

Is this a rails bug? Or a paranoia bug?

akaspick commented 8 years ago

Looks like it's a Rails bug. Specifically https://github.com/rails/rails/pull/16531

hisatake commented 8 years ago

Is there any solutions when I reference association in a where clause?

sealocal commented 8 years ago
class Chapter < ActiveRecord::Base
  acts_as_paranoid

  # This would override the deleted scope at all times
  # I believe the opened issue was intended for optionally unscoping the query
  belongs_to :book, -> { with_deleted }
  # As far as I can tell, this simply does not work in Rail 4.0.x
  belongs_to :book_with_deleted, -> { unscope(where: :deleted_at) }, class_name: 'Book', foreign_key: :book_id

  # I've found this to be the best option for my application so far.
  # This method returns the Book that this Chapter belongs to, whether it was deleted or not,
  # but is explicit, just like calling with_deleted on a relation.
  def deleted_book
    Book.with_deleted.find( self.chapter_id )
  end
end
wynksaiddestroy commented 7 years ago

The only working solution I've found so far is to manually specify the join:

Chapter.joins('INNER JOIN "books" ON "books"."id" = "chapters"."book_id"')
dgmora commented 7 years ago

Reading through the issue I don't fully understand if this is a bug in paranoia, a bug in rails, or an expected behaviour. The linked issues for rails are closed, so I assume that's not blocking anything. I would expect that if a record is deleted, when I access a deleted object's belongs_to or a has_many, I get either all records, or the deleted records. If I have a deleted post and I access a user post.user, the user is nil. with_deleted on the association fails.

Currently I'm using a workaround like this:

# post.rb

belongs_to :post

def user
  deleted? ? User.unscoped { super } : super
end

I'm using paranoia 2.3.1 and rails 5.1.2.

Anyway, thanks for the great job your're doing here guys! 🙂

Schwad commented 6 years ago

It's January 2018 (happy 4th anniversary guys!) and I just wanted to write a massive thank you to @wynksaiddestroy for the solution two comments above, nothing else but that worked!

I was in a situation with MyObject.joins(:related_deleted_objects).first and not getting any my_objects where the related_deleted_object was destroyed. whew! :)

wynksaiddestroy commented 6 years ago

@Schwad: You're welcome. Always happy to help someone out.

jason0415 commented 6 years ago

I'm on Rails 5.0.7. Bellowing code works for me.

class Client
  has_one :contact_detail, -> { unscope(where: :deleted_at) }
end

class ContactDetail
  belongs_to :client, with_deleted: true
end

When you call Client.last.contact_detail, it will give you deleted contact_detail.