Django DoesNotExist Error: Complete Guide to Understanding and Fixing
Comments
Sign in to join the conversation
If you're working with Django, you've almost certainly encountered this error:
ModelName.DoesNotExist: ModelName matching query does not exist.
This is one of the most common exceptions in Django, and understanding how to handle it properly is crucial for building robust applications.
DoesNotExist is an exception that Django raises when you try to retrieve a single database object that doesn't exist. It's Django's way of telling you: "I looked for this record in the database, and it's not there."
Every Django model automatically gets its own DoesNotExist exception class as an inner class.
The DoesNotExist exception is raised by methods that expect to return exactly one object:
.get().latest().earliest()The most common scenario is using .get() with a query that returns nothing.
from django.contrib.auth.models import User
# PROBLEMATIC: User might not exist
user = User.objects.get(username='johndoe')
# If 'johndoe' doesn't exist: User.DoesNotExist exception
Why it fails: The .get() method expects exactly one match. If there are zero matches, it raises DoesNotExist.
Fix Option 1: Try/Except (Error Handling)
from django.contrib.auth.models import User
try:
user = User.objects.get(username='johndoe')
print(f"Found user: {user.email}")
except User.DoesNotExist:
print("User not found")
user = None # or handle appropriately
Fix Option 2: Use .filter().first() (Returns None)
user = User.objects.filter(username='johndoe').first()
if user:
print(f"Found user: {user.email}")
else:
print("User not found")
Fix Option 3: Use get_object_or_404() in Views
from django.shortcuts import get_object_or_404
def user_profile(request, username):
# Automatically returns 404 if user doesn't exist
user = get_object_or_404(User, username=username)
return render(request, 'profile.html', {'user': user})
Getting objects by ID is very common, but the ID might not exist.
from myapp.models import Article
# PROBLEMATIC
article = Article.objects.get(pk=999)
# Article.DoesNotExist if ID 999 doesn't exist
Fix in Views:
from django.shortcuts import get_object_or_404
def article_detail(request, article_id):
article = get_object_or_404(Article, pk=article_id)
return render(request, 'article.html', {'article': article})
Fix in General Code:
try:
article = Article.objects.get(pk=article_id)
except Article.DoesNotExist:
# Handle missing article
return None # or create one, log error, etc.
Accessing reverse relationships can also raise DoesNotExist.
# models.py
class User(models.Model):
username = models.CharField(max_length=100)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
# Usage
user = User.objects.get(username='alice')
# PROBLEMATIC: Profile might not exist for this user
profile = user.profile
# Profile.DoesNotExist if no profile exists
Fix Option 1: Try/Except
try:
bio = user.profile.bio
except Profile.DoesNotExist:
bio = "No profile yet"
Fix Option 2: hasattr() Check
if hasattr(user, 'profile'):
bio = user.profile.bio
else:
bio = "No profile yet"
Fix Option 3: Use getattr() with Default
profile = getattr(user, 'profile', None)
if profile:
bio = profile.bio
else:
bio = "No profile yet"
Fix Option 4: Create Profile Automatically with Signals
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
These methods also raise DoesNotExist if no records exist.
from myapp.models import BlogPost
# PROBLEMATIC: Raises DoesNotExist if table is empty
latest_post = BlogPost.objects.latest('published_date')
Fix:
try:
latest_post = BlogPost.objects.latest('published_date')
except BlogPost.DoesNotExist:
latest_post = None
# Or use filter with ordering
latest_post = BlogPost.objects.order_by('-published_date').first()
from myapp.models import Order
# PROBLEMATIC: Might not find any matching orders
order = Order.objects.get(
user=request.user,
status='pending',
created_date__gte=timezone.now() - timedelta(days=7)
)
Fix: Use filter().first() for complex conditions
order = Order.objects.filter(
user=request.user,
status='pending',
created_date__gte=timezone.now() - timedelta(days=7)
).first()
if order:
# Process order
pass
else:
# No matching order found
pass
.get() + Try/Except When:def fetch_user_data(user_id):
try:
user = User.objects.get(pk=user_id)
return serialize_user(user)
except User.DoesNotExist:
logger.warning(f"User {user_id} not found")
return None
get_object_or_404() When:from django.shortcuts import get_object_or_404
def product_view(request, product_id):
product = get_object_or_404(Product, pk=product_id)
return render(request, 'product.html', {'product': product})
.filter().first() When:Nonedef get_user_settings(user):
settings = UserSettings.objects.filter(user=user).first()
return settings or get_default_settings()
.filter().exists() When:if Order.objects.filter(user=request.user, status='pending').exists():
messages.warning(request, "You already have a pending order")
Sometimes you need to handle both DoesNotExist and MultipleObjectsReturned.
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
return "No user found with that email"
except User.MultipleObjectsReturned:
return "Multiple users found - data integrity issue!"
Generic Exception Handling:
from django.core.exceptions import ObjectDoesNotExist
try:
user = User.objects.get(username=username)
except ObjectDoesNotExist: # Catches DoesNotExist from any model
user = None
Here's a complete example showing best practices:
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import User, Profile, Post
@login_required
def user_profile(request, username):
# Get user or 404 - user should exist
user = get_object_or_404(User, username=username)
# Profile might not exist - use try/except
try:
profile = user.profile
except Profile.DoesNotExist:
# Create profile on the fly
profile = Profile.objects.create(
user=user,
bio="No bio yet"
)
In DRF, handle DoesNotExist differently:
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
class UserDetailAPI(APIView):
def get(self, request, user_id):
try:
user = User.objects.get(pk=user_id)
serializer = UserSerializer(user)
return Response(serializer.data)
except User.DoesNotExist:
return Response(
{'error': 'User not found'},
status=status.HTTP_404_NOT_FOUND
)
Or use DRF's built-in:
from rest_framework.generics import RetrieveAPIView
from rest_framework.exceptions import NotFound
class UserDetailAPI(RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_field = 'pk'
def get_object(self):
try:
return super().get_object()
except User.DoesNotExist:
raise NotFound('User not found')
Write tests to ensure your code handles missing objects:
from django.test import TestCase
from myapp.models import User
class UserViewTests(TestCase):
def test_nonexistent_user_returns_404(self):
response = self.client.get('/user/99999/')
self.assertEqual(response.status_code, 404)
def test_get_or_none_returns_none(self):
user = User.objects.filter(pk=99999).first()
self.assertIsNone(user)
# BAD: Can raise exception on each iteration
for user_id in user_ids:
user = User.objects.get(pk=user_id) # Dangerous!
process_user(user)
Fix: Bulk fetch
# GOOD: Single query, handles missing IDs gracefully
users = User.objects.filter(pk__in=user_ids)
for user in users:
process_user(user)
# BAD: Can raise DoesNotExist
def get_user_bio(user):
return user.profile.bio # Assumes profile exists
Fix:
# GOOD
def get_user_bio(user):
try:
return user.profile.bio
except Profile.DoesNotExist:
return "No bio available"
# WRONG: Generic Exception is too broad
try:
user = User.objects.get(pk=user_id)
except Exception: # Catches ALL exceptions, even bugs!
user = None
Fix:
# CORRECT: Be specific
try:
user = User.objects.get(pk=user_id)
except User.DoesNotExist:
user = None
Do you need exactly one object?
│
├─ YES → In a view?
│ ├─ YES → Use get_object_or_404()
│ └─ NO → Use .get() with try/except
│
└─ NO → OK if zero matches?
├─ YES → Use .filter().first()
└─ NO → Use .get() with try/except
The DoesNotExist exception is Django's way of handling missing database records. The key takeaways:
get_object_or_404() for clean 404 handling.get() with try/except for custom handling.filter().first() to avoid exceptions.filter().exists() for performanceModel.DoesNotExist, not generic ExceptionRemember: The goal isn't to avoid the exception, but to handle it appropriately for your use case. A missing database record is often a normal condition, not an error - plan for it!