Skip to main content

Preview cascade deletes before execution and enforce safe deletion with explicit confirmation.

Project description

django-delete-preview

Preview cascade deletes before execution and enforce safe deletion with explicit confirmation.

Features

  • Inspect the full cascade delete tree for model instances and QuerySets
  • Get a structured preview (JSON or human-readable text) before deleting
  • Block accidental deletes on instances: instance.delete() raises unless you pass confirm=True
  • For QuerySets, use delete_with_preview(confirm=True) — the standard .delete() on QuerySets is not overridden
  • Django management command for inspecting from the command line
  • Works with any Django model — no schema changes required
  • Python 3.10+, Django 4.2+, fully typed

Installation

pip install django-delete-preview

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    "django_delete_preview",
]

Quickstart

from django_delete_preview.mixins import DeletePreviewMixin
from django_delete_preview.managers import DeletePreviewManager
from django.db import models


class Author(DeletePreviewMixin, models.Model):
    name = models.CharField(max_length=200)
    objects = DeletePreviewManager()


class Book(models.Model):
    title = models.CharField(max_length=300)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

Usage

Instance preview

author = Author.objects.get(pk=1)
summary = author.preview_delete()
# {
#   "database": "default",
#   "total_objects": 3,
#   "models": {"myapp.Author": 1, "myapp.Book": 2},
#   "items": {"myapp.Author": ["Jane Austen"], "myapp.Book": ["Oliver Twist", "Great Expectations"]}
# }

Safe instance delete

# Without confirmation → raises ValueError
author.delete()
# ValueError: Delete blocked. Run preview_delete() first and confirm the operation.

# With confirmation → executes
author.delete(confirm=True)

QuerySet preview

summary = Author.objects.filter(active=False).preview_delete()

QuerySet safe delete

Use delete_with_preview() — the standard Django .delete() is not overridden by this library, so Django admin bulk actions and other internal framework usage continue to work normally.

# Without confirmation → raises ValueError
Author.objects.filter(active=False).delete_with_preview()

# With confirmation → executes
Author.objects.filter(active=False).delete_with_preview(confirm=True)

# Standard .delete() still works normally (no protection)
Author.objects.filter(active=False).delete()

Management command

# Preview (text output)
python manage.py preview_delete myapp.Author 1

# Preview (JSON output)
python manage.py preview_delete myapp.Author 1 --format json

# Preview + execute
python manage.py preview_delete myapp.Author 1 --execute

JSON output format

{
  "database": "default",
  "total_objects": 10,
  "models": {
    "myapp.Author": 1,
    "myapp.Order": 9
  }
}

Configuration

# settings.py — maximum number of item string representations per model (default: 100)
DELETE_PREVIEW_MAX_ITEMS = 50

Limitations

  • Does not support soft delete — objects are permanently deleted
  • Does not preview pre_delete / post_delete signal side effects
  • fast_deletes (objects deleted without loading into memory) are counted but may include approximate str() representations

Example app

The repository includes a fully runnable Django project under example/ that demonstrates every feature of the library using a bookstore domain (Author, Book, Order, Publisher).

Setup

git clone https://github.com/adrianomargarin/django-delete-preview
cd django-delete-preview
pip install -e ".[dev]"

cd example
python manage.py migrate
python manage.py loaddata initial_data   # loads 3 authors, 9 books, 5 orders, 3 publishers

Instance preview

python manage.py shell
from bookstore.models import Author

author = Author.objects.get(pk=1)   # Jane Austen
author.preview_delete()
# {
#   "database": "default",
#   "total_objects": 6,
#   "models": {"bookstore.Author": 1, "bookstore.Book": 3, "bookstore.Order": 2},
#   "items": {
#     "bookstore.Author": ["Jane Austen"],
#     "bookstore.Book": ["Emma", "Persuasion", "Pride and Prejudice"],
#     "bookstore.Order": ["ORD-001", "ORD-002"]
#   }
# }

Safe instance delete

author.delete()
# ValueError: Delete blocked. Run preview_delete() first and confirm the operation.

author.delete(confirm=True)
# (6, {"bookstore.Book": 3, "bookstore.Order": 2, "bookstore.Author": 1})

QuerySet preview

from bookstore.models import Author

Author.objects.all().preview_delete()
# {
#   "database": "default",
#   "total_objects": 20,
#   "models": {"bookstore.Author": 3, "bookstore.Book": 9, "bookstore.Order": 5},
#   ...
# }

QuerySet safe delete

Author.objects.all().delete_with_preview()
# ValueError: Delete blocked. Run preview_delete() first and confirm the operation.

Author.objects.all().delete_with_preview(confirm=True)
# (20, {"bookstore.Book": 9, "bookstore.Order": 5, "bookstore.Author": 3})

Management command

# Human-readable preview
python manage.py preview_delete bookstore.Author 1

# Database    : default
# Total items : 6
#
# Models affected:
#   bookstore.Author: 1
#   bookstore.Book: 3
#   bookstore.Order: 2
#
# Items:
#   bookstore.Author:
#     - Jane Austen
#   bookstore.Book:
#     - Emma
#     - Persuasion
#     - Pride and Prejudice
#   bookstore.Order:
#     - ORD-001
#     - ORD-002

# JSON preview
python manage.py preview_delete bookstore.Author 1 --format json

# Preview and execute
python manage.py preview_delete bookstore.Author 1 --execute

Publisher (no cascade)

Publisher has no related models, so deleting one affects only itself:

from bookstore.models import Publisher

pub = Publisher.objects.get(pk=1)
pub.preview_delete()
# {"database": "default", "total_objects": 1, "models": {"bookstore.Publisher": 1}, ...}

pub.delete(confirm=True)

Development setup

git clone https://github.com/adrianomargarin/django-delete-preview
cd django-delete-preview
pip install -e ".[dev]"
pre-commit install

# Run tests
pytest

# Lint
ruff check src tests

# Type check
mypy src/django_delete_preview

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_delete_preview-0.0.3.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_delete_preview-0.0.3-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file django_delete_preview-0.0.3.tar.gz.

File metadata

  • Download URL: django_delete_preview-0.0.3.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_delete_preview-0.0.3.tar.gz
Algorithm Hash digest
SHA256 9006c6ddab1796b73cf91c0eca2b5ded86dd760328307c728426b861515e7266
MD5 b69c0edb8f7440bcb512941ee5b47479
BLAKE2b-256 6309335016f4f2168c0d1d98d57c6181d1fa42730f6dd0487bd6ce786eaf1e80

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_delete_preview-0.0.3.tar.gz:

Publisher: release.yml on adrianomargarin/django-delete-preview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_delete_preview-0.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for django_delete_preview-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 eaca687cb82dcdccc16ff5ad43d2237f14491cae8063828a2064286992a96abe
MD5 c9effe39f96c519d3eadb62d3a591239
BLAKE2b-256 ef50434ba4974193daec15730b8df4461acbb9976c8bd360e46aa4f20f7aecb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_delete_preview-0.0.3-py3-none-any.whl:

Publisher: release.yml on adrianomargarin/django-delete-preview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page