Django
models
delete record
Python
tutorial

How to delete a record in Django models?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Django, deleting a record means deleting a model instance through the ORM. The basic API is simple, but the real behavior depends on whether you delete one instance or a queryset, and on how related models are configured through ForeignKey and on_delete.

Delete a Single Model Instance

The most direct pattern is:

python
1from blog.models import Article
2
3article = Article.objects.get(pk=10)
4article.delete()

delete() removes that row from the database and returns a tuple describing how many objects were deleted and which model types were affected.

If the object may not exist, use exception handling:

python
1from django.core.exceptions import ObjectDoesNotExist
2from blog.models import Article
3
4try:
5    article = Article.objects.get(pk=10)
6    article.delete()
7except ObjectDoesNotExist:
8    print("Article not found")

In a view, get_object_or_404() is often cleaner:

python
1from django.shortcuts import get_object_or_404, redirect
2from blog.models import Article
3
4def delete_article(request, pk):
5    article = get_object_or_404(Article, pk=pk)
6    article.delete()
7    return redirect("article-list")

Delete Multiple Records with a QuerySet

If you want to delete more than one row, call delete() on a queryset:

python
1from blog.models import Article
2
3deleted_count, details = Article.objects.filter(is_archived=True).delete()
4print(deleted_count)
5print(details)

This is efficient because Django can translate it into a bulk delete operation. It is usually better than fetching each row and deleting them one by one unless you explicitly need per-object custom logic.

That distinction matters because bulk deletion may bypass some instance-level behavior you expected from loading each object manually.

Deletion behavior becomes more important when other models point to the record. Consider:

python
1from django.db import models
2
3class Author(models.Model):
4    name = models.CharField(max_length=100)
5
6
7class Article(models.Model):
8    author = models.ForeignKey(Author, on_delete=models.CASCADE)
9    title = models.CharField(max_length=200)

If you delete an Author, Django will also delete related Article rows because on_delete=models.CASCADE says dependent rows should be removed.

Other common on_delete behaviors include:

  • 'PROTECT, which blocks the delete'
  • 'SET_NULL, which clears the relation if the field allows null=True'
  • 'SET_DEFAULT, which assigns a default value'

Do not treat deletion as an isolated operation. Check the relation graph first.

Wrap Important Deletes in a Transaction

If deletion is part of a larger workflow, use a transaction so the database stays consistent:

python
1from django.db import transaction
2from shop.models import Order
3
4with transaction.atomic():
5    order = Order.objects.get(pk=42)
6    order.delete()

This is especially useful when you combine deletes with updates, logging records, or follow-up inserts that must succeed or fail together.

Soft Delete Versus Hard Delete

In many business systems, a hard delete is not the best choice. You may need auditability or the ability to restore records later. A common alternative is soft delete, where the row stays in the table and you mark it as inactive.

python
1from django.db import models
2
3class Customer(models.Model):
4    email = models.EmailField()
5    is_deleted = models.BooleanField(default=False)
6
7    def soft_delete(self):
8        self.is_deleted = True
9        self.save(update_fields=["is_deleted"])

That design changes queries too, because active views must filter out deleted rows consistently.

Common Pitfalls

The biggest mistake is deleting a parent object without checking related rows. CASCADE can remove more data than expected.

Another problem is confusing queryset deletion with instance deletion. Queryset deletes are efficient, but if you expected custom per-instance code or signal behavior tied to loading objects individually, verify that your approach matches that requirement.

Developers also sometimes expose delete actions through GET requests. That is unsafe. Destructive actions should normally be triggered through POST and protected with CSRF validation.

Finally, remember that delete() is permanent unless you designed a recovery path. If the application needs reversibility, use soft delete or database backups rather than hoping the row can be reconstructed later.

Summary

  • Delete one object with instance.delete().
  • Delete many objects with queryset.delete().
  • Check ForeignKey relationships and on_delete behavior before removing data.
  • Use transactions when deletes are part of a larger workflow.
  • Prefer soft delete when the business domain requires recovery or auditing.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.