Git Product home page Git Product logo

Comments (16)

sevaorlov avatar sevaorlov commented on July 1, 2024 4

I faced the same problem. Not sure what is the best approach, record.destroyed? is true after we call record.destroy, but rails checks that now in update_column.
Workaround is to clear paranoid_value before updating the column.

self.paranoid_value = nil
update_column self.class.paranoid_column, nil

from acts_as_paranoid.

surjay avatar surjay commented on July 1, 2024 3

Yes, I meant immutable apart from recovery. So the soft-deleted record is immutable until it is recovered (or fully destroyed).

What about cases where schema changes or other data migrations need to happen? Certainly, you can get around validations/callbacks while doing those if necessary but there are also times where you'd want validations/callbacks to run while also being able to update/adjust soft-deleted records.

from acts_as_paranoid.

mvz avatar mvz commented on July 1, 2024 3

In general, you should not depend on a Rails app's real models when migrating.

@mvz It's not about depending on the model in a migration. What if I add a non-nullable field and want to backfill records with calculated values?

That is indeed a valid use-case. When I wrote that comment I was only thinking of actions inside Rails migrations, and also I was of the opinion that all data migrations like backfilling should happen inside Rails migrations. In practice, this is not always possible, especially when there are many existing records.

The question is: what is the reason for making this library opinionated about this?

It is kind of an accident: ActsAsParanoid first implemented #destroyed? to return true for soft-deleted records, and then later Rails made destroyed records immutable.

I also don't see any connection between soft deletion and immutability. Two completely different topics and they shouldn't be confused.

There is a tension here: On the one hand we want soft-deleted records to behave for the most part like actually destroyed objects, and Rails considers destroyed objects immutable. On the other hand we want to allow things that are not possible for actually destroyed records:

  1. We should be able to restore a record
  2. We should be able to update a soft-deleted record, for example to keep it valid, or to satisfy some new database constraint

I see two ways to facilitate item 2:

  1. Make #destroyed? return false, and #deleted? return true for soft-deleted records. This is a simple solution but is a breaking change. Also, there may be unexpected changes in behavior in Rails because it only looks at #destroyed?.
  2. Provide some kind of context where soft-deleted records can be updated, for example:
ActsAsParanoid.allow_updating_soft_deleted_records do
   soft_deleted.update(new_column: some_value)
end

from acts_as_paranoid.

mvz avatar mvz commented on July 1, 2024 1

What about cases where schema changes or other data migrations need to happen?

In general, you should not depend on a Rails app's real models when migrating. If you need some model, you should create a nested class inside the migration for it, like so:

class DoSomething < ActiveRecord::Migration[6.1]
  class Foo < ActiveRecord::Base
    # You can put any validations here, but leave out acts_as_paranoid if that makes sense.
  end

  def up
    Foo.find_each do |foo|
      Foo.update(...)
    end
  end

  # ...
end

from acts_as_paranoid.

thisismydesign avatar thisismydesign commented on July 1, 2024 1

What about cases where schema changes or other data migrations need to happen?

In general, you should not depend on a Rails app's real models when migrating.

@mvz It's not about depending on the model in a migration. What if I add a non-nullable field and want to backfill records with calculated values? There are all sorts of reasons to update soft-deleted records.

The question is: what is the reason for making this library opinionated about this? Shouldn't it be as general-purpose as possible to serve as many users as possible? I also don't see any connection between soft deletion and immutability. Two completely different topics and they shouldn't be confused.

from acts_as_paranoid.

zzak avatar zzak commented on July 1, 2024

I'm not sure the best way to work around this atm, open to suggestions in the form of a patch.

It seems like there's a reason for this behavior though, I would consider destroyed records to be immutable. But that could just be an edge case that was never thought of.

from acts_as_paranoid.

sandipransing avatar sandipransing commented on July 1, 2024

Any updates on this?

from acts_as_paranoid.

surjay avatar surjay commented on July 1, 2024

Was there any consensus on a workaround or patch idea for this?

from acts_as_paranoid.

mvz avatar mvz commented on July 1, 2024

I agree with @zzak that destroyed records should be immutable.

from acts_as_paranoid.

anars73 avatar anars73 commented on July 1, 2024

I agree with @zzak that destroyed records should be immutable.

I don't agree. There are cases where some content might need to be soft-deleted and restored afterwards.
Edit: since acts_as_paranoid 0.6.3 there is a recover method which can be invoked on records for recovery.

from acts_as_paranoid.

mvz avatar mvz commented on July 1, 2024

Yes, I meant immutable apart from recovery. So the soft-deleted record is immutable until it is recovered (or fully destroyed).

from acts_as_paranoid.

anujbiyani avatar anujbiyani commented on July 1, 2024

I've got a maybe-legitimate reason to update a soft-destroyed record:
Take an app with posts and comments on those posts (aka simple threading), and the ability to soft-destroy a post. I have an API that wants to sort threads (posts and their comments) based on latest activity. To do this, any time someone creates a post or comments on a post, a column on the post is updated with a timestamp.

If the post is soft-destroyed, and someone comments on that post, then I can't update the timestamp for tracking latest activity.

Here's pseudocode that demonstrates this (I haven't tested this exact code, but my app's code is very similar):

class Post < ApplicationRecord
  has_many :comments
  acts_as_paranoid
end

class Comment < ApplicationRecord
  belongs_to :post
  after_commit :update_thread_activity

  def update_thread_activity
    post.update_column(:thread_activity, DateTime.current)
  end
end

post = Post.create
post.destroy # soft-destroys

post.comments.create # fails because `post.destroyed?` is `true`

I could move this sort of "thread activity" tracking to a class like FooThread, but that feels like a less-than-ideal workaround.

Is this a better use case?

from acts_as_paranoid.

thisismydesign avatar thisismydesign commented on July 1, 2024

@sevaorlov's proposal will undelete the records, don't do that..

entity.paranoid_value = nil
entity.update!(attribute: value)

from acts_as_paranoid.

mvz avatar mvz commented on July 1, 2024

@anujbiyani I don't understand how someone would comment on a post that has been soft-deleted? If an object would still be interacted with but just has a different status, acts_as_paranoid is not really suitable.

from acts_as_paranoid.

thisismydesign avatar thisismydesign commented on July 1, 2024

@mvz Thanks for explaining. Option 2 would be sufficient I think. Option 1 would be better in terms of no surprises when updating deleted records, but might be worse in terms of other surprises.

from acts_as_paranoid.

phillipn avatar phillipn commented on July 1, 2024

You can also use this combination to get around the dreaded cannot update a destroyed record:

  after_commit :set_field_after_destroy, on: :destroy

  def set_field_after_destroy
    Model.with_deleted.where(id: id).update_all(field: false)
  end

from acts_as_paranoid.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.