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 passconfirm=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_deletesignal side effects fast_deletes(objects deleted without loading into memory) are counted but may include approximatestr()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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9006c6ddab1796b73cf91c0eca2b5ded86dd760328307c728426b861515e7266
|
|
| MD5 |
b69c0edb8f7440bcb512941ee5b47479
|
|
| BLAKE2b-256 |
6309335016f4f2168c0d1d98d57c6181d1fa42730f6dd0487bd6ce786eaf1e80
|
Provenance
The following attestation bundles were made for django_delete_preview-0.0.3.tar.gz:
Publisher:
release.yml on adrianomargarin/django-delete-preview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_delete_preview-0.0.3.tar.gz -
Subject digest:
9006c6ddab1796b73cf91c0eca2b5ded86dd760328307c728426b861515e7266 - Sigstore transparency entry: 1204789619
- Sigstore integration time:
-
Permalink:
adrianomargarin/django-delete-preview@5fee81f3ade785e3b273dea8e9acb8e2430767c4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/adrianomargarin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5fee81f3ade785e3b273dea8e9acb8e2430767c4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file django_delete_preview-0.0.3-py3-none-any.whl.
File metadata
- Download URL: django_delete_preview-0.0.3-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eaca687cb82dcdccc16ff5ad43d2237f14491cae8063828a2064286992a96abe
|
|
| MD5 |
c9effe39f96c519d3eadb62d3a591239
|
|
| BLAKE2b-256 |
ef50434ba4974193daec15730b8df4461acbb9976c8bd360e46aa4f20f7aecb3
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_delete_preview-0.0.3-py3-none-any.whl -
Subject digest:
eaca687cb82dcdccc16ff5ad43d2237f14491cae8063828a2064286992a96abe - Sigstore transparency entry: 1204789624
- Sigstore integration time:
-
Permalink:
adrianomargarin/django-delete-preview@5fee81f3ade785e3b273dea8e9acb8e2430767c4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/adrianomargarin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5fee81f3ade785e3b273dea8e9acb8e2430767c4 -
Trigger Event:
workflow_dispatch
-
Statement type: