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)