Skip to main content

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

Metadata

Release files for django-delete-preview 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-delete-preview 0.0.3
File Size Uploaded
django_delete_preview-0.0.3.tar.gz 18.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-delete-preview 0.0.3
File Interpreter ABI Platform
django_delete_preview-0.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 29.8 kB

Release files / django_delete_preview-0.0.3.tar.gz

Download URL django_delete_preview-0.0.3.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9006c6ddab1796b73cf91c0eca2b5ded86dd760328307c728426b861515e7266
BLAKE2b-256 checksum
How to use checksums
6309335016f4f2168c0d1d98d57c6181d1fa42730f6dd0487bd6ce786eaf1e80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 1, 2026.

Transparency log

Release files / django_delete_preview-0.0.3-py3-none-any.whl

Download URL django_delete_preview-0.0.3-py3-none-any.whl
Size 11.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eaca687cb82dcdccc16ff5ad43d2237f14491cae8063828a2064286992a96abe
BLAKE2b-256 checksum
How to use checksums
ef50434ba4974193daec15730b8df4461acbb9976c8bd360e46aa4f20f7aecb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.3 This release

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page