question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SoftDelete is an anti-pattern

See original GitHub issue

My apologies if this comes across as grumpy. I don’t mean to be disrespectful. I know what it’s like to maintain a popular project and no one likes it when someone pipes up about how they don’t like something.

With that said though, there’s a very real problem here with the SoftDeletableModel.

Problem

The SoftDeletableModel (and corresponding manager) has been the bane of my life for years. I started one job and spent a year removing it from the codebase, only to start a new job and be faced with it again. The defaults it implements (overriding .objects) break internal operations with Django (specifically the admin) and leads to hundreds of lost hours trying to debug code that’s silently hiding your own data from you.

Example

Consider this simple set of models:

class User(SoftDeletableModel):
    name = models.CharField(max_length=128)

class Purchase(models.Model):
    user = models.ForeignKey(User)
    note = models.TextField()

Now:

  1. Create a user and a purchase with that user.
  2. Soft delete that user
  3. Find the purchase in the admin.
  4. Try to save a change to the note field.

→ Django explodes because the attached user doesn’t exist… except that it does exist but we’ve broken the defaults of the underlying framework by overriding .objects.all() to mean not actually all.

This problem reasserts itself when debugging: You’re a new developer tasked with generating statistics based on the number of users, and using User.objects.all().count() to do it. For some reason, your math doesn’t add up and you watch a few hours get sucked away by this anti-pattern. I have no idea what would happen in cases where you might do something like Purchase.objects.filter(user__name__startswith="..."), but I’m willing to bet it’ll cause problems when comparing to User.objects.filter(name__startswith="...").

Please don’t muck with the defaults.

Proposed Alternative

Rather than replacing .objects.all(), rework it to leave .all() alone and instead have .available(). Alternatively, leave objects alone altogether and just have an .available_objects manager on the class. In either case, explicit is better than implicit if for no other reason than there’s no surprises.

Understandably, this would break current implementations using the SoftDeletableModel, but frankly not doing this means that more and more of this sort of thing will creep into code everywhere as this is a very popular library and this is being described as a good solution to a common problem. In its current implementation however it creates more problems than it solves.

At the very least, please add a warning to the documentation that using this model as your parent can create long-lasting and hard-to-remove problems with data integrity in your application and disruptions with core functions like the Django admin. I think that if the developers that came before me had seen such a message, they would have thought twice before using it as the default base for all models in the system.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:23
  • Comments:16 (7 by maintainers)

github_iconTop GitHub Comments

4reactions
schinckelcommented, Apr 2, 2019

That’s a really good point: in fact I think the django docs explicitly call out overriding the default manager as potentially dangerous behaviour.

I think if it was to be changed, then it would need a deprecation path, but perhaps this is something that could be done in stages. (Is there a deprecation schedule for this project?)

3reactions
mozz100commented, Apr 10, 2019

I work with @danielquinn. FWIW I wrote a blog post about the kinds of problems that soft deletion can cause. I hope other developers read it and think about the implications of adopting this implementation of soft delete, ideally before they go ahead and adopt it without realising.

https://www.rmorrison.net/mnemozzyne/2019/04/10/think-twice-before-you-soft-delete/

(I don’t think soft delete per se is an antipattern, just that the over-riding of .objects is problematic)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why soft deletes are evil and what to do instead - James Halsall
However, what about the benefits of a soft delete that we lose with a hard delete? The main two are accountability for action...
Read more >
Are soft deletes a good idea? [duplicate] - Stack Overflow
I always soft-delete. In cases where the database needs to be scrubbed of one or more records, I generally employ either a two-step...
Read more >
Soft-deletion is actually pretty hard - Tree Web Solutions
The default scope approach used by acts_as_paranoid has been consistently criticized as an anti-pattern by many Rails developers all the way back to...
Read more >
Soft deletion probably isn't worth it | Hacker News
In my experience soft delete is basically free if you do it from the start. ... Thinking this thru a little bit, I...
Read more >
How to implement XPO's Soft Delete pattern for NHibernate
Soft Delete is a persistence pattern where instead of removing entities ... is a pattern or an antipattern but this is beyond this...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found