DEV Community

Cover image for Building a Production-Ready Soft Delete System in Django (with Custom User Model)
Saveen Kumar
Saveen Kumar

Posted on

Building a Production-Ready Soft Delete System in Django (with Custom User Model)

Soft delete sounds simple—until you're the one implementing it in a real-world, regulated application.

In building a financial portfolio management system, we faced the not-so-fun challenge of handling user deletion without compromising data integrity or violating compliance rules. You can't just delete() a user when audit trails, tax records, and GDPR are watching.

So, here's how we designed a clean, maintainable soft delete system using a custom Django User model.

Problem Summary
Most finance or SaaS platforms need to:

  • Retain user-related transactions for tax/audit purposes
  • Disable login access cleanly
  • Restore accidentally deleted accounts
  • Avoid cascading deletions of historical data Using Django’s built-in User + separate UserProfile quickly turned into a nightmare: joins everywhere, edge cases all over the place, and no easy path to soft delete.

So we followed Django’s best practice: own your User model from day one.

The Solution (With Code)
Here's a quick breakdown of the implementation:

  • ✅ Custom User model based on AbstractUser
  • ✅ Added is_deleted, deleted_at, deleted_by
  • ✅ Overrode the admin to support soft deletion & restoration
  • ✅ Used on_delete=models.PROTECT for critical models like Transaction
  • ✅ Queryset filters and indexes for is_deleted

📄 Full walkthrough (with complete code and admin logic):
👉 Read the detailed article

Key Implementation Tips

  • is_active=False prevents login
  • Soft deletes ≠ just hiding records — handle reversibility and auditing
  • Never on_delete=CASCADE sensitive data like financial history
  • Use admin actions for bulk delete/restore and badge UI for status

Why This Matters
Soft delete isn’t just for compliance. It protects you from:

  • Mistaken deletions
  • Breaking historical reporting
  • GDPR data logic edge cases
  • Limitations of Django’s default User model Plus, migration from default User → custom User later is a huge pain. Better to do it upfront.

💬 Your Turn
Have you implemented soft delete in production? Found better patterns, or do you prefer packages like django-safedelete? Would love to hear your experience or suggestions for scaling this better.

🧵 Or drop thoughts below 👇

Top comments (0)