DEV Community

sizan mahmud0
sizan mahmud0

Posted on

15 Django Questions Every Developer Should Answer in Their Sleep

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, ...)
Enter fullscreen mode Exit fullscreen mode

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:

  1. URL Routing: Request hits urls.py, Django matches URL pattern
  2. Middleware (Request): Runs request middleware in order
  3. View Processing: Executes the matched view function
  4. Template Rendering: Renders template with context data
  5. Middleware (Response): Runs response middleware in reverse order
  6. 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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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!)
Enter fullscreen mode Exit fullscreen mode

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=True only (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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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})
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
# 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']
Enter fullscreen mode Exit fullscreen mode

Usage:

# Development
python manage.py runserver --settings=myproject.settings.dev

# Production
export DJANGO_SETTINGS_MODULE=myproject.settings.prod
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
]
Enter fullscreen mode Exit fullscreen mode

Best practices:

  • Always run migrations in development first
  • Never edit applied migrations
  • Use --fake carefully (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!")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)