Chapter 14: Django Delete Data

1. The Four Main Ways to Delete Data (Quick Comparison Table)

Method What it does Speed (relative) Affects how many? Calls model .delete()? Signals? (pre/post_delete) Cascades relationships? Best for
instance.delete() Delete one object Medium 1 Yes Yes Yes (follows on_delete) Normal CRUD, views, forms
QuerySet.delete() Bulk DELETE – fastest SQL way Very fast (1 query) Many No Yes (but per-object) Yes Mass cleanup, admin actions
bulk_delete() (not built-in) Custom or third-party Fast Many No Optional Depends Rare – usually use QuerySet.delete()
Soft delete (custom field) Mark as deleted (is_deleted=True) Fast 1 or many No Custom signals No real delete Production apps (audit, undo)

Django 6.0.1 note: No major changes to deletion since 4.x/5.x — QuerySet.delete() still emits pre_delete/post_delete signals for each object (can be slow on large sets), and cascading still respects on_delete.

2. Method 1: Delete One Object – instance.delete()

Most common in views/admin.

In shell (python manage.py shell):

Python

What really happens?

  1. Django collects all related objects (follows ForeignKey/ManyToMany)
  2. For each related object → calls .delete() recursively (if CASCADE)
  3. Emits pre_delete signal for each object
  4. Executes SQL DELETEs
  5. Emits post_delete signal for each

Because we used on_delete=models.CASCADE on Choice → deleting Question also deletes all its Choices automatically.

In a view example (e.g. delete poll):

Python

Add CSRF + confirmation template in real code!

3. Method 2: Bulk Delete – QuerySet.delete() (Fast & Dangerous!)

One SQL statement — no per-object .delete() calls.

Python

Important behaviors:

  • Still cascades → deletes related objects according to on_delete
  • Emits pre_delete and post_delete signals for each object (can be slow if 10,000+ objects!)
  • Does not call custom .delete() overrides on models

When to prefer this: Admin actions, cleanup scripts, management commands.

Danger: Accidentally Question.objects.all().delete() → wipes entire table + cascades everywhere!

Always filter + .count() first:

Python

4. on_delete Options – What Happens When Parent is Deleted?

This is critical — controls child behavior when parent deleted.

In your Choice model:

Python

Common options (all from django.db.models):

  • models.CASCADE (default) → Delete parent → auto-delete all children Example: Delete Question → all its Choices gone
  • models.PROTECT → Raise ProtectedError if children exist
    Python

    → Try q.delete() → error if choices exist → safe for important relations

  • models.SET_NULL → Set child FK to NULL (field must have null=True)
    Python

    → Delete Question → Choice.question becomes None (keeps choices)

  • models.SET_DEFAULT → Set to default value (field must have default=…)
  • models.DO_NOTHING → Do nothing → database error if constraint violation
  • models.SET(value_or_callable) → Set to specific value or callable result

2026 best practice: Use PROTECT for most user-generated content (prevent accidental wipes), CASCADE only when children are useless without parent (comments without post), SET_NULL for audit/history.

5. Soft Delete – Very Popular in Production (Don’t Really Delete!)

Instead of hard delete → add field + filter everywhere.

Python

Then override manager:

Python

Now Question.objects.all() excludes deleted ones automatically.

Use all_objects for admin if needed.

Pros: Undo possible, audit trail, no cascade surprises Cons: DB grows, queries need care

Many 2026 projects (especially SaaS) use soft delete + celery cleanup.

6. Common Mistakes & Real Fixes

  • Cascade deleted too much → Forgot to check on_delete → use PROTECT in dev
  • Slow bulk delete → Signals per object → use raw SQL or transaction.atomic() + disable signals temporarily (advanced)
  • IntegrityError on delete → DO_NOTHING without DB cascade → change to CASCADE/PROTECT
  • Deleted wrong records → No confirmation → always use POST + confirm page in views

Quick Exercise

  1. Create 2 questions + 3 choices each
  2. Delete one question → see choices gone (CASCADE)
  3. Change to on_delete=models.PROTECT → migrate → try delete → error
  4. Try bulk Question.objects.filter(question_text__contains=”biryani”).delete()

Tell me:

  • “Worked! Show soft delete implementation in detail”
  • “How to delete with confirmation in view/template?”
  • “What if I want to delete without cascade?”
  • “Error ProtectedError — how to handle?”
  • Or next topic: “Django Forms” / “Authentication” / “Full polls voting”

You’re handling the full CRUD now — super solid foundation! 💪🚀 Let’s keep building! 🇮🇳

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *