Comments (16)
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.
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.
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:
- We should be able to restore a record
- 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:
- 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?
. - 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.
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.
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.
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.
Any updates on this?
from acts_as_paranoid.
Was there any consensus on a workaround or patch idea for this?
from acts_as_paranoid.
I agree with @zzak that destroyed records should be immutable.
from acts_as_paranoid.
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.
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.
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.
@sevaorlov's proposal will undelete the records, don't do that..
entity.paranoid_value = nil
entity.update!(attribute: value)
from acts_as_paranoid.
@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.
@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.
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)
- Emulate difference between `destroy` and `destroy!` in default ActiveRecord HOT 4
- Avoid loading associated object for counter cache updates
- Behavior of destroy on associations does not match default ActiveRecord
- Setting `:double_tap_destroys_fully` should apply to methods on relations as well HOT 1
- Remove deprecated methods for the 0.8.0 release
- Association building broken with upgrade to 0.7.0 HOT 4
- FeatureRequest: Global default options configuration
- Calling delete then recover on the same object doesn't recover has_many/dependent relationships HOT 2
- Honor foreign key constraint violation HOT 2
- Release with Rails 7 support HOT 3
- Question: Skip recover functionality HOT 6
- Recover when junction table record exists rather than creating a new one
- recover! method raises validation error even if record is valid. Using recover works properly
- Possible conflict between acts_as_paranoid and newrelic_rpm
- with_deleted unscopes everything when joining through multiple associations HOT 3
- `only_deleted` doesn't work sometimes(?)
- How can I destroy models without destroying their ActiveStorage associations? HOT 4
- Why can not use like this User.first.update!(deleted_at: Time.now) HOT 1
- Add `with_deleted: true` for has_many relationships HOT 1
- The documentation for `column_type: 'boolean'`, allow_nulls option is incorrect
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from acts_as_paranoid.