Toolmingo
Guides22 min read

50 Django Interview Questions (With Answers)

Top Django interview questions with clear answers and code examples — covering ORM, views, templates, REST framework, authentication, caching, signals, and deployment.

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 matching
  • include() — delegates to another urls.py
  • name= — enables reverse() 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:

  1. field.to_python() — convert raw input to Python type
  2. field.validate() — built-in validators
  3. field.run_validators() — custom validators on the field
  4. form.clean_<fieldname>() — per-field clean method
  5. form.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_ROOT directly at /static/
  • WhiteNoise — Python middleware that serves static from Django process
  • CDN/S3 — upload collected files to S3, set STATIC_URL to 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=listdefault=[] 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?

  1. Install Django Debug Toolbar (dev) — shows all SQL queries per request
  2. Use django.db.connection.queries in shell
  3. Use EXPLAIN ANALYZE on PostgreSQL for slow queries
  4. Add indexes on frequently filtered columns
  5. Use select_related/prefetch_related to 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.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools