Master These and You'll Never Fail Another Django Interview
You've built Django projects. You've deployed apps. You call yourself a Django developer.
But can you explain why Django does what it does? Can you answer the questions that separate juniors from seniors?
These 15 questions come up in every serious Django interview, code review, and production debugging session. If you can't answer them confidently, you're not ready for that senior role.
Let's fix that.
1. What's the Difference Between select_related() and prefetch_related()?
Short Answer:
select_related() does SQL JOINs. prefetch_related() does separate queries.
The Real Answer:
# select_related - Use for ForeignKey and OneToOne
# Single query with SQL JOIN
books = Book.objects.select_related('author').all()
# SQL: SELECT * FROM book INNER JOIN author ON book.author_id = author.id
# prefetch_related - Use for ManyToMany and reverse ForeignKey
# Two separate queries
authors = Author.objects.prefetch_related('books').all()
# SQL: SELECT * FROM author
# SQL: SELECT * FROM book WHERE author_id IN (1, 2, 3, ...)
When to use which:
-
select_related(): When you need related data and the relationship is OneToOne or ForeignKey -
prefetch_related(): When dealing with ManyToMany or reverse ForeignKey relationships
Why it matters: Using the wrong one can cause N+1 query problems that kill performance.
2. Explain Django's Request-Response Cycle
The Flow:
-
URL Routing: Request hits
urls.py, Django matches URL pattern - Middleware (Request): Runs request middleware in order
- View Processing: Executes the matched view function
- Template Rendering: Renders template with context data
- Middleware (Response): Runs response middleware in reverse order
- HTTP Response: Sends response back to client
# urls.py
path('products/', views.product_list)
↓
# Middleware (SecurityMiddleware, SessionMiddleware, etc.)
↓
# views.py
def product_list(request):
products = Product.objects.all()
return render(request, 'products.html', {'products': products})
↓
# Template rendering
↓
# Response middleware
↓
# HTTP Response to browser
Interview tip: Mention middleware runs twice - once for request, once for response.
3. What Are Django Signals and When Should You Use Them?
What they are:
Signals allow decoupled applications to get notified when actions occur elsewhere.
Common signals:
from django.db.models.signals import post_save, pre_save, pre_delete, post_delete
from django.dispatch import receiver
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
When to use:
- ✅ Logging and auditing
- ✅ Cache invalidation
- ✅ Sending notifications
- ✅ Creating related objects automatically
When NOT to use:
- ❌ Complex business logic (use service layers instead)
- ❌ When direct calls are clearer
- ❌ Performance-critical paths (signals have overhead)
Why developers get this wrong: They overuse signals and create "spooky action at a distance" that's hard to debug.
4. What's the Difference Between null=True and blank=True?
This trips up even experienced developers.
class Product(models.Model):
name = models.CharField(max_length=100) # Required in forms AND database
description = models.TextField(blank=True) # Optional in forms, empty string in DB
discount = models.DecimalField(null=True, blank=True) # Optional everywhere
notes = models.TextField(null=True) # NULL in DB, but required in forms (bad!)
The breakdown:
-
null=True: Database level - allows NULL in database -
blank=True: Validation level - allows empty values in forms
Best practice:
- For strings (CharField, TextField): Use
blank=Trueonly (store empty string, not NULL) - For other fields (IntegerField, DateField): Use both
null=True, blank=True
Why: Having NULL and empty strings for the same "no value" concept creates confusion.
5. Explain Django's ORM N+1 Problem
The Problem:
# This looks innocent but is TERRIBLE
authors = Author.objects.all() # 1 query
for author in authors:
print(author.books.count()) # N queries (one per author)
If you have 100 authors, that's 101 database queries!
The Solution:
# Option 1: prefetch_related
authors = Author.objects.prefetch_related('books').all()
for author in authors:
print(author.books.count()) # No additional queries!
# Option 2: annotate
from django.db.models import Count
authors = Author.objects.annotate(book_count=Count('books'))
for author in authors:
print(author.book_count) # Single query total
How to detect: Django Debug Toolbar or django.db.connection.queries
6. What Are Django Middlewares and Give Real Examples
Definition:
Middleware is a framework of hooks into Django's request/response processing.
Real-world examples:
# middleware.py
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code executed before the view
print(f"Request: {request.method} {request.path}")
response = self.get_response(request)
# Code executed after the view
print(f"Response status: {response.status_code}")
return response
# Custom authentication middleware
class CustomAuthMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
token = request.headers.get('Authorization')
if token:
request.user = authenticate_token(token)
return self.get_response(request)
Common use cases:
- Authentication and authorization
- Request/response logging
- CORS headers
- Rate limiting
- Performance monitoring
7. What's the Difference Between get() and filter() in Django ORM?
# get() - Returns single object or raises exception
try:
user = User.objects.get(id=1) # Returns User object
except User.DoesNotExist:
user = None
except User.MultipleObjectsReturned:
# Handle multiple results
# filter() - Always returns QuerySet (even if empty or single result)
users = User.objects.filter(id=1) # Returns QuerySet[User]
if users.exists():
user = users.first()
When to use what:
-
get(): When you expect exactly one result (or want an exception) -
filter(): When you might get zero, one, or many results
Common mistake:
# DON'T DO THIS
user = User.objects.get(email=email) # Crashes if email doesn't exist
# DO THIS
user = User.objects.filter(email=email).first() # Returns None if not found
8. Explain Django's F() and Q() Objects
F() - Database-level field operations:
from django.db.models import F
# Increment all product prices by 10%
Product.objects.update(price=F('price') * 1.1)
# Compare two fields in the same model
Article.objects.filter(views__gt=F('likes') * 2)
# Avoids race conditions
product.quantity = F('quantity') - 1
product.save()
Q() - Complex queries:
from django.db.models import Q
# OR condition
User.objects.filter(Q(email=email) | Q(username=username))
# Complex nested conditions
Product.objects.filter(
Q(category='electronics') &
(Q(price__lt=1000) | Q(discount__gt=20))
)
# NOT condition
Product.objects.filter(~Q(status='deleted'))
Why they matter: They enable complex queries without raw SQL.
9. What Are Django Managers and When to Create Custom Ones?
Default Manager:
class Product(models.Model):
name = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
objects = models.Manager() # Default manager
Custom Manager:
class ActiveProductManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
class Product(models.Model):
name = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
objects = models.Manager() # All products
active = ActiveProductManager() # Only active products
# Usage
Product.objects.all() # All products
Product.active.all() # Only active products
When to use:
- Repeated filter patterns
- Business logic encapsulation
- Cleaner, more readable code
10. Explain Class-Based Views vs Function-Based Views
Function-Based Views:
def product_list(request):
products = Product.objects.all()
return render(request, 'products.html', {'products': products})
def product_detail(request, pk):
product = get_object_or_404(Product, pk=pk)
return render(request, 'product.html', {'product': product})
Class-Based Views:
from django.views.generic import ListView, DetailView
class ProductListView(ListView):
model = Product
template_name = 'products.html'
context_object_name = 'products'
class ProductDetailView(DetailView):
model = Product
template_name = 'product.html'
When to use:
- FBV: Simple views, custom logic, better for beginners
- CBV: CRUD operations, code reuse, built-in functionality
The truth: Use what makes your code clearer. CBVs aren't always better.
11. What's the Purpose of django.conf.settings and How to Organize Settings?
The problem with single settings file:
- Development vs Production confusion
- Secrets in version control
- Can't easily switch environments
The solution:
settings/
├── __init__.py
├── base.py # Common settings
├── dev.py # Development settings
├── prod.py # Production settings
└── test.py # Test settings
# base.py
INSTALLED_APPS = [...]
MIDDLEWARE = [...]
# dev.py
from .base import *
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# prod.py
from .base import *
import os
DEBUG = False
SECRET_KEY = os.environ.get('SECRET_KEY')
ALLOWED_HOSTS = ['yourdomain.com']
Usage:
# Development
python manage.py runserver --settings=myproject.settings.dev
# Production
export DJANGO_SETTINGS_MODULE=myproject.settings.prod
12. What Are Django Migrations and How to Handle Migration Conflicts?
What they are:
Version control for your database schema.
Common commands:
python manage.py makemigrations # Create migration files
python manage.py migrate # Apply migrations
python manage.py showmigrations # Show migration status
python manage.py sqlmigrate app_name 0001 # Show SQL for migration
Handling conflicts:
When two developers create migrations simultaneously:
# You'll see
CommandError: Conflicting migrations detected; multiple leaf nodes
# Solution 1: Create merge migration
python manage.py makemigrations --merge
# Solution 2: Manually edit migration dependencies
# In the migration file:
dependencies = [
('myapp', '0004_auto_20231115_1234'),
('myapp', '0004_auto_20231115_5678'), # The conflicting one
]
Best practices:
- Always run migrations in development first
- Never edit applied migrations
- Use
--fakecarefully (or never) - Keep migrations small and atomic
13. Explain Django's Content Types Framework
What it is:
A way to track all models in your project and create generic relationships.
Real-world use case:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Comment(models.Model):
# Can comment on any model
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
# Usage
from blog.models import Post
from products.models import Product
post = Post.objects.get(id=1)
Comment.objects.create(content_object=post, text="Great post!")
product = Product.objects.get(id=5)
Comment.objects.create(content_object=product, text="Love this product!")
Use cases:
- Comments/Likes on multiple models
- Activity feeds
- Tagging system
- Audit logs
14. What's Django's select_for_update() and When to Use It?
The problem - Race condition:
# Two users try to buy the last item simultaneously
product = Product.objects.get(id=1)
if product.stock > 0:
product.stock -= 1 # Race condition here!
product.save()
The solution:
from django.db import transaction
with transaction.atomic():
product = Product.objects.select_for_update().get(id=1)
if product.stock > 0:
product.stock -= 1
product.save()
What it does:
Locks the database row until the transaction completes. Other requests wait.
When to use:
- Financial transactions
- Inventory management
- Ticket booking systems
- Any operation where data consistency is critical
Warning: Can cause deadlocks if not used carefully.
15. Explain Django's Caching Framework and Strategies
Cache levels:
# 1. Per-site cache (cache everything)
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
# 2. Per-view cache
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 15 minutes
def product_list(request):
return render(request, 'products.html')
# 3. Template fragment cache
{% load cache %}
{% cache 500 sidebar %}
<!-- expensive template code -->
{% endcache %}
# 4. Low-level cache API
from django.core.cache import cache
products = cache.get('products')
if products is None:
products = Product.objects.all()
cache.set('products', products, 300)
Cache backends:
- Development: Dummy cache or local memory
- Production: Redis or Memcached
When to cache:
- ✅ Expensive database queries
- ✅ API responses from external services
- ✅ Computed values (reports, statistics)
- ❌ User-specific sensitive data
- ❌ Rapidly changing data
Final Thoughts
If you can explain these 15 concepts clearly, you're not just a Django developer—you're a proficient Django developer.
But here's the thing: knowing the answers isn't enough. You need to understand why Django works this way and when to apply each concept.
The difference between a junior and senior developer? Juniors know what tools exist. Seniors know which tool to use when.
Now go build something amazing.
Which question surprised you the most? Which ones do you still struggle with? Drop a comment—let's discuss!
Top comments (0)