Django interviews test your understanding of the ORM, MVT architecture, security built-ins, REST API design, and production deployment patterns. This guide covers 50 common questions — with concise answers and code examples.
Quick reference
| Topic | Most asked questions |
|---|---|
| Architecture | MVT pattern, request/response cycle, settings |
| Models & ORM | QuerySets, relationships, migrations, managers |
| Views & URLs | FBVs vs CBVs, URL routing, middleware |
| Templates | Context, inheritance, custom tags/filters |
| Forms | ModelForm, validation, CSRF |
| Auth & Security | Authentication, permissions, CSRF, XSS |
| DRF | Serializers, viewsets, routers, authentication |
| Performance | select_related, prefetch_related, caching, DB indexing |
| Testing | TestCase, Client, mocking, fixtures |
| Advanced | Signals, custom managers, async, Celery |
Architecture
1. What is Django's MVT pattern?
Django follows Model-View-Template (MVT):
| Layer | Responsibility | Django component |
|---|---|---|
| Model | Data layer — defines schema, ORM | models.py |
| View | Business logic — handles request/response | views.py |
| Template | Presentation — renders HTML | templates/ |
The URL dispatcher (urls.py) routes incoming requests to the appropriate View. The View queries Models and passes data to Templates.
Django's "View" is equivalent to a Controller in MVC — the Template replaces the View in classic MVC.
2. Describe the Django request/response lifecycle.
1. Browser sends HTTP request
2. WSGI/ASGI server (Gunicorn/Uvicorn) receives it
3. Django middleware stack processes request (top-down)
4. URL resolver matches the path → finds the view function/class
5. View executes: queries DB, applies business logic
6. Template rendered (or serializer for APIs)
7. Response object created
8. Middleware stack processes response (bottom-up)
9. HTTP response sent to browser
Middleware can short-circuit the cycle (e.g., SecurityMiddleware redirects HTTP → HTTPS before the view runs).
3. What is INSTALLED_APPS and why does order matter?
INSTALLED_APPS registers Django apps so they are included in migrations, template discovery, and admin registration:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'myapp',
]
Order matters for:
- Template loaders: the first app whose template matches wins
- Migrations: dependent apps must appear before apps that extend them
AppConfig.ready(): called in order, used for signal registration
4. What is settings.py splitting and why do you do it?
For multiple environments you split settings into a package:
config/
settings/
__init__.py
base.py # shared settings
local.py # DEBUG=True, SQLite, no email sending
production.py # DEBUG=False, PostgreSQL, S3, real email
# production.py
from .base import *
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST'),
}
}
Switch with DJANGO_SETTINGS_MODULE=config.settings.production.
Models & ORM
5. What is a QuerySet and how is it lazy?
A QuerySet is a collection of database queries that has not yet been executed:
# No DB query here
users = User.objects.filter(is_active=True).order_by('date_joined')
# DB query fires here (evaluation)
for user in users: # iteration
print(user.email)
count = users.count() # SELECT COUNT(*) — separate query
list(users) # SELECT * — forces evaluation
QuerySets are lazy because they cache results once evaluated. You can chain filters without hitting the DB multiple times.
6. Explain select_related vs prefetch_related.
| Method | Best for | SQL generated |
|---|---|---|
select_related |
ForeignKey / OneToOne (forward) | JOIN — single query |
prefetch_related |
ManyToMany / reverse ForeignKey | Separate query + Python join |
# BAD — N+1 problem
for order in Order.objects.all():
print(order.customer.name) # separate DB hit per order
# GOOD — select_related for FK
for order in Order.objects.select_related('customer'):
print(order.customer.name) # single JOIN query
# GOOD — prefetch_related for M2M
for product in Product.objects.prefetch_related('tags'):
print(product.tags.all()) # 2 queries total
7. What are Django model relationships and when to use each?
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
# Many Books → One Author (ForeignKey)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
# Many-to-Many: a book can have many genres, a genre many books
genres = models.ManyToManyField('Genre', blank=True)
class AuthorProfile(models.Model):
# One-to-One: each author has one profile
author = models.OneToOneField(Author, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField()
| Type | Use case |
|---|---|
ForeignKey |
Many-to-one (book → author) |
OneToOneField |
Extend a model (user → profile) |
ManyToManyField |
Many-to-many (books ↔ genres) |
8. What does on_delete do and what options exist?
on_delete controls what happens to the referencing object when the referenced object is deleted:
| Option | Behaviour |
|---|---|
CASCADE |
Delete the referencing object too |
SET_NULL |
Set FK to null (requires null=True) |
SET_DEFAULT |
Set FK to the field's default value |
PROTECT |
Raise ProtectedError — prevents deletion |
RESTRICT |
Similar to PROTECT with stricter cascade rules |
DO_NOTHING |
No action (may break DB integrity) |
9. How do Django migrations work?
# 1. Detect model changes → generate migration file
python manage.py makemigrations
# 2. Apply migrations to DB
python manage.py migrate
# 3. Show migration status
python manage.py showmigrations
# 4. Rollback to a specific migration
python manage.py migrate myapp 0003
Migration files are Python code in migrations/. Each migration has operations (CreateModel, AddField, AlterField, RunPython, etc.) and dependencies. Django tracks which migrations have run in the django_migrations table.
10. What is a custom model manager?
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class Article(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, default='draft')
objects = models.Manager() # default manager
published = PublishedManager() # custom manager
# Usage
Article.published.all() # only published articles
Article.objects.all() # all articles
Custom managers let you encapsulate common filters as named QuerySets.
11. What is Meta class in a Django model?
class Article(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-created_at'] # default sort (newest first)
verbose_name = 'Article'
verbose_name_plural = 'Articles'
indexes = [
models.Index(fields=['author', 'created_at']),
]
constraints = [
models.UniqueConstraint(fields=['title', 'author'], name='unique_title_per_author'),
]
db_table = 'blog_articles' # custom table name
12. Explain F() and Q() objects.
F() — reference a field value in a query (avoids race conditions):
from django.db.models import F
# Atomic increment — no read-modify-write in Python
Product.objects.filter(pk=1).update(stock=F('stock') - 1)
# Compare two fields
Product.objects.filter(price__gt=F('cost') * 1.5)
Q() — complex lookups with OR / NOT:
from django.db.models import Q
# Articles by Alice OR tagged as 'featured'
Article.objects.filter(Q(author__name='Alice') | Q(tags__name='featured'))
# NOT draft
Article.objects.filter(~Q(status='draft'))
Views & URLs
13. What is the difference between FBVs and CBVs?
| Function-Based Views (FBV) | Class-Based Views (CBV) | |
|---|---|---|
| Syntax | Simple function | Class inheriting from a View |
| HTTP methods | Manual if request.method == 'POST' |
Separate get(), post() methods |
| Reuse | Decorators | Mixins and inheritance |
| Built-ins | — | ListView, DetailView, CreateView, etc. |
| Readability | Simple logic | Better for complex, CRUD-heavy views |
# FBV
def article_list(request):
articles = Article.objects.all()
return render(request, 'articles/list.html', {'articles': articles})
# CBV equivalent
from django.views.generic import ListView
class ArticleListView(ListView):
model = Article
template_name = 'articles/list.html'
context_object_name = 'articles'
14. How does URL routing work in Django?
# urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
path('articles/<int:pk>/', views.article_detail, name='article-detail'),
path('articles/<slug:slug>/', views.article_by_slug),
path('search/', views.search, name='search'),
]
path()— exact/typed matching (int,slug,uuid,str,path)re_path()— regex matchinginclude()— delegates to anotherurls.pyname=— enablesreverse()and{% url %}in templates
# Reverse a named URL in Python
from django.urls import reverse
url = reverse('article-detail', kwargs={'pk': 42}) # '/articles/42/'
15. What is middleware and how do you write one?
Middleware processes every request/response globally — logging, authentication checks, security headers, etc.
# myapp/middleware.py
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response # called once at startup
def __call__(self, request):
import time
start = time.time()
response = self.get_response(request) # calls the next middleware/view
duration = time.time() - start
response['X-Request-Duration'] = str(duration)
return response
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'myapp.middleware.TimingMiddleware', # your middleware
# ...
]
Middleware runs top-down on request, bottom-up on response.
16. What is get_object_or_404?
from django.shortcuts import get_object_or_404
def article_detail(request, pk):
# Raises Http404 if not found — instead of Article.DoesNotExist
article = get_object_or_404(Article, pk=pk, status='published')
return render(request, 'articles/detail.html', {'article': article})
It's equivalent to:
try:
article = Article.objects.get(pk=pk, status='published')
except Article.DoesNotExist:
raise Http404
Templates
17. How does Django template inheritance work?
<!-- base.html -->
<!DOCTYPE html>
<html>
<head><title>{% block title %}My Site{% endblock %}</title></head>
<body>
{% block content %}{% endblock %}
</body>
</html>
<!-- articles/list.html -->
{% extends "base.html" %}
{% block title %}Articles{% endblock %}
{% block content %}
{% for article in articles %}
<h2>{{ article.title }}</h2>
<p>{{ article.body|truncatewords:50 }}</p>
{% empty %}
<p>No articles yet.</p>
{% endfor %}
{% endblock %}
{% extends %} must be the first tag. {% block %} defines overridable regions. Child templates fill blocks; everything else in the parent is inherited.
18. What are custom template tags and filters?
# myapp/templatetags/myfilters.py
from django import template
register = template.Library()
@register.filter(name='bold')
def bold(value):
return f'<strong>{value}</strong>'
@register.simple_tag
def site_name():
return "My Awesome Site"
@register.inclusion_tag('myapp/partials/latest_articles.html')
def show_latest_articles(count=5):
return {'articles': Article.objects.order_by('-created_at')[:count]}
{% load myfilters %}
{{ article.title|bold }}
{% site_name %}
{% show_latest_articles 3 %}
Forms
19. What is the difference between Form and ModelForm?
# Form — manual field definition
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
# ModelForm — fields from model definition
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'body', 'status']
# or: exclude = ['author', 'created_at']
widgets = {
'body': forms.Textarea(attrs={'rows': 10}),
}
ModelForm.save() creates or updates the model instance directly. It also inherits model-level validators.
20. How does form validation work?
Django validates in this order:
field.to_python()— convert raw input to Python typefield.validate()— built-in validatorsfield.run_validators()— custom validators on the fieldform.clean_<fieldname>()— per-field clean methodform.clean()— cross-field validation
class RegistrationForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput)
confirm = forms.CharField(widget=forms.PasswordInput)
def clean_password(self):
pw = self.cleaned_data['password']
if len(pw) < 8:
raise forms.ValidationError("Password must be 8+ characters.")
return pw
def clean(self):
cleaned = super().clean()
if cleaned.get('password') != cleaned.get('confirm'):
raise forms.ValidationError("Passwords do not match.")
return cleaned
Authentication & Security
21. How does Django's built-in authentication work?
from django.contrib.auth import authenticate, login, logout
def login_view(request):
if request.method == 'POST':
user = authenticate(
request,
username=request.POST['username'],
password=request.POST['password'],
)
if user is not None:
login(request, user) # sets session cookie
return redirect('dashboard')
else:
messages.error(request, 'Invalid credentials.')
return render(request, 'auth/login.html')
def logout_view(request):
logout(request)
return redirect('home')
@login_required decorator redirects unauthenticated users to settings.LOGIN_URL:
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
return render(request, 'dashboard.html')
22. What is Django's permission system?
# Check permission in a view
@permission_required('myapp.can_publish', raise_exception=True)
def publish_article(request, pk):
...
# In Python code
if request.user.has_perm('myapp.can_publish'):
...
# Assign permission to user/group
from django.contrib.auth.models import Permission
perm = Permission.objects.get(codename='can_publish')
user.user_permissions.add(perm)
Django creates four default permissions per model: add_<model>, change_<model>, delete_<model>, view_<model>. Custom permissions are defined in Meta.permissions.
23. How does Django protect against CSRF?
Django includes CsrfViewMiddleware by default. Every POST/PUT/DELETE form must include a CSRF token:
<form method="POST">
{% csrf_token %}
...
</form>
This renders as <input type="hidden" name="csrfmiddlewaretoken" value="...">. The middleware checks that the token in the form matches the token in the cookie.
For AJAX:
fetch('/api/endpoint/', {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') },
body: JSON.stringify(data),
});
Exempt views with @csrf_exempt (use with caution).
24. How does Django prevent SQL injection?
Django's ORM uses parameterized queries by default — values are always passed as parameters, never interpolated into SQL strings:
# SAFE — Django ORM escapes automatically
User.objects.filter(username=username)
# Generates: SELECT ... WHERE username = %s (with username as parameter)
# SAFE — raw() with parameters
User.objects.raw('SELECT * FROM auth_user WHERE username = %s', [username])
# DANGEROUS — never do this
User.objects.raw(f'SELECT * FROM auth_user WHERE username = {username}')
25. What security settings should you configure for production?
# settings/production.py
DEBUG = False
SECRET_KEY = env('DJANGO_SECRET_KEY') # never hardcode
# HTTPS
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# Cookies
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
# Clickjacking
X_FRAME_OPTIONS = 'DENY'
# Content type sniffing
SECURE_CONTENT_TYPE_NOSNIFF = True
Django REST Framework
26. What is a DRF serializer?
Serializers convert complex Python objects (model instances, QuerySets) to/from JSON:
from rest_framework import serializers
class ArticleSerializer(serializers.ModelSerializer):
author_name = serializers.SerializerMethodField()
class Meta:
model = Article
fields = ['id', 'title', 'body', 'status', 'author_name', 'created_at']
read_only_fields = ['created_at']
def get_author_name(self, obj):
return obj.author.get_full_name()
def validate_title(self, value):
if len(value) < 5:
raise serializers.ValidationError("Title too short.")
return value
# Serialize
serializer = ArticleSerializer(article)
data = serializer.data # OrderedDict
# Deserialize + create
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(author=request.user)
27. What is the difference between APIView and ViewSet?
APIView |
ViewSet + Router |
|
|---|---|---|
| HTTP methods | Manual get(), post(), etc. |
list(), create(), retrieve(), update(), destroy() |
| URL registration | Manual path() |
Router generates URLs automatically |
| Boilerplate | More control | Less code for CRUD |
# ViewSet
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
# Router
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('articles', ArticleViewSet)
urlpatterns = [path('api/', include(router.urls))]
# Generates: /api/articles/, /api/articles/{pk}/
28. What are DRF permissions?
from rest_framework.permissions import BasePermission
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in ('GET', 'HEAD', 'OPTIONS'):
return True
return obj.author == request.user
class ArticleViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
| Built-in permission | Behaviour |
|---|---|
AllowAny |
No restriction |
IsAuthenticated |
Must be logged in |
IsAdminUser |
Must be is_staff=True |
IsAuthenticatedOrReadOnly |
Read: anyone; Write: authenticated |
DjangoModelPermissions |
Maps to Django model permissions |
29. How do you implement pagination in DRF?
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
# Custom pagination
class LargeResultsSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = 'page_size'
max_page_size = 1000
# Response structure:
# {
# "count": 1023,
# "next": "https://api.example.com/articles/?page=2",
# "previous": null,
# "results": [...]
# }
30. How do you handle authentication in DRF?
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication', # web clients
'rest_framework.authentication.TokenAuthentication', # mobile/API clients
'rest_framework_simplejwt.authentication.JWTAuthentication', # JWT
],
}
Token authentication (built-in):
# Obtain token
POST /api/token/ with username + password → {"token": "abc123"}
# Use token
Authorization: Token abc123
JWT (djangorestframework-simplejwt):
POST /api/token/ → {"access": "...", "refresh": "..."}
POST /api/token/refresh/ with refresh → new access token
Authorization: Bearer <access_token>
Performance & Caching
31. What is the N+1 query problem?
# BAD — 1 query for orders + N queries for customers
for order in Order.objects.all():
print(order.customer.name) # hits DB per order
# GOOD — 2 queries total
for order in Order.objects.select_related('customer'):
print(order.customer.name)
# Check queries in shell
from django.db import connection
print(len(connection.queries))
# In Django Debug Toolbar (dev) — shows all SQL queries per request
32. How do you add a database index in Django?
class Article(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, db_index=True) # simple index
slug = models.SlugField(unique=True) # unique index
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
# Composite index
models.Index(fields=['author', 'status'], name='author_status_idx'),
# Partial index (PostgreSQL)
models.Index(
fields=['created_at'],
condition=Q(status='published'),
name='published_created_idx',
),
]
33. How does Django caching work?
# settings.py — Redis cache backend
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
from django.core.cache import cache
# Low-level cache API
cache.set('article_list', articles, timeout=300) # 5 min
articles = cache.get('article_list')
cache.delete('article_list')
# Per-view cache
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # cache for 15 minutes
def article_list(request):
...
# Template fragment cache
{% load cache %}
{% cache 300 article_detail article.pk %}
... expensive rendering ...
{% endcache %}
34. What is only() and defer() in QuerySets?
# Load only specific columns — reduces data transfer
articles = Article.objects.only('title', 'slug', 'created_at')
# Load all columns EXCEPT body (large text field)
articles = Article.objects.defer('body')
# Accessing a deferred field triggers an extra query per object
# Use annotate() for computed values instead
from django.db.models import Count
articles = Article.objects.annotate(comment_count=Count('comments'))
Testing
35. How do you write Django tests?
from django.test import TestCase, Client
from django.contrib.auth.models import User
from myapp.models import Article
class ArticleModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user('alice', password='pass')
def test_article_creation(self):
article = Article.objects.create(
title='Test Article',
body='Content',
author=self.user,
)
self.assertEqual(str(article), 'Test Article')
self.assertEqual(Article.objects.count(), 1)
class ArticleViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('alice', password='pass')
def test_list_view_requires_login(self):
response = self.client.get('/articles/')
self.assertRedirects(response, '/accounts/login/?next=/articles/')
def test_authenticated_user_sees_articles(self):
self.client.login(username='alice', password='pass')
response = self.client.get('/articles/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'articles/list.html')
36. What is APIClient in DRF testing?
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
class ArticleAPITest(APITestCase):
def setUp(self):
self.user = User.objects.create_user('alice', password='pass')
self.client = APIClient()
def test_create_article_authenticated(self):
self.client.force_authenticate(user=self.user)
data = {'title': 'New Article', 'body': 'Body text', 'status': 'draft'}
response = self.client.post('/api/articles/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Article.objects.count(), 1)
def test_create_article_unauthenticated(self):
response = self.client.post('/api/articles/', {})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
37. How do you use fixtures and factories?
Fixtures (JSON/YAML data loaded into DB):
python manage.py dumpdata myapp.Article > articles.json
python manage.py loaddata articles.json
class ArticleTest(TestCase):
fixtures = ['articles.json']
Factory Boy (preferred — programmatic):
import factory
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
username = factory.Sequence(lambda n: f'user{n}')
email = factory.LazyAttribute(lambda u: f'{u.username}@example.com')
password = factory.PostGenerationMethodCall('set_password', 'password')
class ArticleFactory(factory.django.DjangoModelFactory):
class Meta:
model = Article
title = factory.Faker('sentence', nb_words=5)
author = factory.SubFactory(UserFactory)
status = 'published'
Advanced
38. What are Django signals and when to use them?
Signals allow decoupled code to react to events that happen elsewhere in the app:
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)
@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
if created:
send_email(instance.email, 'Welcome!')
Built-in signals: pre_save, post_save, pre_delete, post_delete, m2m_changed, request_started, request_finished.
Caution: Signals make code harder to trace. Prefer direct function calls when the relationship is within the same app. Use signals for cross-app decoupling.
39. What is select_for_update()?
from django.db import transaction
@transaction.atomic
def transfer_funds(from_account_id, to_account_id, amount):
# Lock rows until end of transaction — prevents race conditions
accounts = Account.objects.select_for_update().filter(
pk__in=[from_account_id, to_account_id]
)
from_acc = accounts.get(pk=from_account_id)
to_acc = accounts.get(pk=to_account_id)
if from_acc.balance < amount:
raise ValueError("Insufficient funds")
from_acc.balance = F('balance') - amount
to_acc.balance = F('balance') + amount
from_acc.save()
to_acc.save()
SELECT ... FOR UPDATE prevents other transactions from modifying the locked rows until the current transaction commits.
40. How do Django transactions work?
from django.db import transaction
# Option 1: atomic() as decorator
@transaction.atomic
def create_order(user, items):
order = Order.objects.create(user=user)
for item in items:
OrderItem.objects.create(order=order, **item)
Product.objects.filter(pk=item['product_id']).update(
stock=F('stock') - item['quantity']
)
# If any exception is raised, the entire transaction rolls back
# Option 2: atomic() as context manager
def create_order(user, items):
with transaction.atomic():
order = Order.objects.create(user=user)
# ...
# Savepoints — nested atomics
with transaction.atomic():
create_user(...)
with transaction.atomic():
create_profile(...) # can roll back just this savepoint
41. What is Django's contenttypes framework?
contenttypes provides a generic way to relate one model to any other model:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Comment(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id') # not a DB column
text = models.TextField()
# Usage
article = Article.objects.first()
comment = Comment(content_object=article, text='Great post!')
comment.save()
# Reverse lookup
comments = Comment.objects.filter(
content_type=ContentType.objects.get_for_model(Article),
object_id=article.pk
)
Used by Django's admin, permissions system, and many third-party apps.
42. How do you implement full-text search in Django?
PostgreSQL full-text search (built-in):
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
def search_articles(query):
vector = SearchVector('title', weight='A') + SearchVector('body', weight='B')
search_query = SearchQuery(query)
return (
Article.objects
.annotate(rank=SearchRank(vector, search_query))
.filter(rank__gte=0.1)
.order_by('-rank')
)
Database index for search:
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
class Article(models.Model):
search_vector = SearchVectorField(null=True, blank=True)
class Meta:
indexes = [GinIndex(fields=['search_vector'])]
43. What is Django's async support?
Django supports async views and ORM operations (Django 4.1+):
import asyncio
from django.http import JsonResponse
async def async_view(request):
# Use sync_to_async for ORM access in async context
from asgiref.sync import sync_to_async
articles = await sync_to_async(list)(
Article.objects.filter(status='published')[:10]
)
return JsonResponse({'count': len(articles)})
# Or use aget() / afilter() / asave() (Django 4.1+)
async def get_article(request, pk):
try:
article = await Article.objects.aget(pk=pk)
except Article.DoesNotExist:
raise Http404
return JsonResponse({'title': article.title})
You need ASGI server (Uvicorn, Daphne) instead of WSGI (Gunicorn) to benefit from async.
44. How do you use Celery with Django?
# celery.py
from celery import Celery
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
# settings.py
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
# tasks.py
from celery import shared_task
@shared_task
def send_newsletter(user_id):
user = User.objects.get(pk=user_id)
# send email...
# In a view — trigger async task
send_newsletter.delay(user.id)
send_newsletter.apply_async(args=[user.id], countdown=60) # run after 60s
# Start worker
# celery -A myproject worker -l info
45. How do you implement custom AbstractUser?
# models.py — in a dedicated users app
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
phone = models.CharField(max_length=20, blank=True)
# settings.py — MUST be set before first migration
AUTH_USER_MODEL = 'users.User'
# In other models
from django.conf import settings
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
Always define AUTH_USER_MODEL before running the first migration. Changing it later requires significant effort.
Production & Deployment
46. How do you serve static files in production?
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles' # collected static files
STATICFILES_DIRS = [BASE_DIR / 'static'] # source static files
# Collect all static files
# python manage.py collectstatic
In production, Django does not serve static files (DEBUG=False). Use:
- Nginx/Apache — serve
STATIC_ROOTdirectly at/static/ - WhiteNoise — Python middleware that serves static from Django process
- CDN/S3 — upload collected files to S3, set
STATIC_URLto CDN URL
# WhiteNoise (simple, no separate server needed)
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # right after SecurityMiddleware
...
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
47. How do you use environment variables in Django?
# pip install django-environ
import environ
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(BASE_DIR / '.env')
DEBUG = env('DEBUG')
SECRET_KEY = env('SECRET_KEY')
DATABASE_URL = env('DATABASE_URL')
DATABASES = {
'default': env.db() # parses DATABASE_URL automatically
}
EMAIL_HOST = env('EMAIL_HOST', default='localhost')
.env file (never commit to git):
DEBUG=False
SECRET_KEY=your-long-random-secret-key
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
48. What are common Django performance optimisations?
| Problem | Solution |
|---|---|
| N+1 queries | select_related(), prefetch_related() |
| Full table scan | Add db_index=True or Meta.indexes |
| Repeated expensive queries | Cache with Redis (cache.set) |
| Loading unused columns | only() or defer() |
| Counting entire QuerySet | count() instead of len() |
| Template re-render | {% cache %} fragment caching |
| Slow endpoints | Profiling with Django Debug Toolbar, Silk |
| High DB load | Connection pooling (pgBouncer), read replicas |
| Large QuerySets in memory | .iterator() — streams rows one by one |
49. How do you use django-debug-toolbar?
# settings/local.py
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE = ['debug_toolbar.middleware.DebugToolbarMiddleware'] + MIDDLEWARE
INTERNAL_IPS = ['127.0.0.1']
# urls.py (development only)
if settings.DEBUG:
import debug_toolbar
urlpatterns = [path('__debug__/', include(debug_toolbar.urls))] + urlpatterns
Django Debug Toolbar shows: SQL queries (count + time), template context, cache hits, signals, headers, settings. Essential for catching N+1 problems during development.
50. What is the difference between null=True and blank=True?
| Setting | Effect | When to use |
|---|---|---|
null=True |
Allows NULL in the database column |
Nullable FK, numeric fields |
blank=True |
Allows empty string "" in form validation |
Optional text fields |
| Both | Field optional in forms AND DB allows NULL | Optional fields on models |
class Profile(models.Model):
# OK: CharField stores empty string "", no need for null
bio = models.TextField(blank=True)
# FK needs null=True to be optional
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
# DateField — use null=True for optional dates, not empty string
birthday = models.DateField(null=True, blank=True)
Avoid null=True on string-based fields (CharField, TextField) — Django convention is to use blank=True and store "" instead of NULL to avoid two empty states.
Anti-patterns
| Anti-pattern | Problem | Fix |
|---|---|---|
| Business logic in templates | Untestable, mixed concerns | Move to models or service layer |
| N+1 queries | 1 query per row → slow | select_related / prefetch_related |
null=True on CharField |
Two empty states ("" and NULL) |
Use blank=True only |
| Mutable default in field | Shared between all instances | default=list → default=[] is wrong — use default=list |
Hardcoded settings.AUTH_USER_MODEL |
Breaks extensibility | Always use get_user_model() |
| Fat views | Untestable logic mixed with request handling | Move business logic to models/services |
No __str__ on models |
<Article object (1)> in admin and shell |
Always define __str__ |
CharField for boolean |
'yes'/'no' strings |
Use BooleanField |
Django vs Flask vs FastAPI
| Django | Flask | FastAPI | |
|---|---|---|---|
| Type | Full-stack framework | Micro-framework | Async API framework |
| ORM | Built-in (Django ORM) | Choose your own | Choose your own (SQLAlchemy) |
| Admin | Built-in | Flask-Admin | None built-in |
| Auth | Built-in | Flask-Login | FastAPI-Users |
| REST API | Django REST Framework | Flask-RESTX | Built-in |
| Async | Django 3.1+ | Via extensions | Native (Starlette) |
| Best for | Full-stack web apps, rapid CRUD, admin tools | Simple APIs, lightweight services | High-performance async APIs |
| Learning curve | Steep | Gentle | Moderate |
Common mistakes
| Mistake | Impact | Fix |
|---|---|---|
Forgetting select_related |
N+1 queries, slow pages | Profile queries with Debug Toolbar |
Storing secrets in settings.py |
Security breach | Use environment variables |
DEBUG=True in production |
Error pages expose code | Always DEBUG=False in prod |
| Not running migrations on deploy | DB schema mismatch | Add migrate to deployment pipeline |
Using pk as predictable ID in URLs |
IDOR vulnerability | Use UUIDs or tokens for public URLs |
| Not validating file uploads | Malicious file execution | Validate extension, MIME type, use S3 |
Forgetting related_name on FK |
article_set clashes when two FKs to same model |
Always set related_name |
| Using signals for tight coupling | Invisible side effects | Use direct calls within same app |
FAQ
Q: When should I use Django vs FastAPI?
Use Django when you need a full-stack solution with ORM, admin panel, auth, and forms out of the box. Use FastAPI for high-performance async microservices or APIs where you want full control over the stack.
Q: Is Django suitable for microservices?
Django can work in microservices but is heavier than Flask or FastAPI. DRF makes building REST APIs straightforward. For pure API microservices without admin/templates, FastAPI or Flask may be lighter.
Q: How do you extend Django's User model?
Best practice: create a custom AbstractUser subclass in a users app and set AUTH_USER_MODEL before the first migration. Avoid using UserProfile linked via OneToOne if possible — it adds a query per user access.
Q: What is __init__.py in the celery config?
# myproject/__init__.py
from .celery import app as celery_app
__all__ = ('celery_app',)
This ensures the Celery app is loaded when Django starts so @shared_task decorators are registered.
Q: How do you debug slow queries in Django?
- Install Django Debug Toolbar (dev) — shows all SQL queries per request
- Use
django.db.connection.queriesin shell - Use
EXPLAIN ANALYZEon PostgreSQL for slow queries - Add indexes on frequently filtered columns
- Use
select_related/prefetch_relatedto eliminate N+1
Q: What is the difference between save() and update()?save() triggers signals (pre_save, post_save) and model clean() — it loads the full object into Python first. update() is a single SQL UPDATE statement, skips signals and Python-level validation, and is faster for bulk updates. Use update() for performance-critical bulk operations, save() when you need signals or validation.