Django DoesNotExist Error: Complete Guide to Understanding and Fixing
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.
What is DoesNotExist?
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.
When Does It Occur?
The DoesNotExist exception is raised by methods that expect to return exactly one object:
.get().latest().earliest()- Accessing a reverse ForeignKey or OneToOne relationship that doesn't exist
Common Scenarios and Solutions
Scenario 1: Basic .get() Query
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})
Scenario 2: Using Primary Keys
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.
Scenario 3: Accessing Related Objects
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)
Scenario 4: latest() and earliest()
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()
Scenario 5: Complex Queries
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
Best Practices: When to Use What
Use .get() + Try/Except When:
- You're in non-view code (utility functions, management commands)
- You need custom error handling
- You want to log the error or perform specific actions
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
Use get_object_or_404() When:
- You're in a Django view
- A missing object should result in a 404 page
- You want clean, readable code
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})
Use .filter().first() When:
- Missing objects are normal/expected
- You don't want to handle exceptions
- You prefer checking for
None
def get_user_settings(user):
settings = UserSettings.objects.filter(user=user).first()
return settings or get_default_settings()
Use .filter().exists() When:
- You only need to check existence
- You don't need the actual object
- Performance matters (avoids loading the object)
if Order.objects.filter(user=request.user, status='pending').exists():
messages.warning(request, "You already have a pending order")
Advanced: Multiple Exception Handling
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
Real-World Example: User Profile Page
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"
)
# Get latest posts - might be empty
latest_posts = Post.objects.filter(author=user).order_by('-created_at')[:5]
# Check if user has any posts (without loading them)
has_posts = Post.objects.filter(author=user).exists()
context = {
'profile_user': user,
'profile': profile,
'latest_posts': latest_posts,
'has_posts': has_posts,
}
return render(request, 'profile.html', context)
Django REST Framework Considerations
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')
Testing for DoesNotExist
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)
Common Mistakes to Avoid
Mistake 1: Using .get() in a Loop
# 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)
Mistake 2: Not Handling Related Object Access
# 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"
Mistake 3: Catching Wrong Exception
# 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
Quick Decision Tree
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
Summary
The DoesNotExist exception is Django's way of handling missing database records. The key takeaways:
- In Views: Use
get_object_or_404()for clean 404 handling - In Utilities: Use
.get()with try/except for custom handling - For Optional Data: Use
.filter().first()to avoid exceptions - For Existence Checks: Use
.filter().exists()for performance - Always be specific: Catch
Model.DoesNotExist, not genericException
Remember: 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!
Comments
Sign in to join the conversation