Django Interview Questions and Answers: 60 Questions from Easy to Hard
This guide is for Indian freshers and experienced Python developers preparing for Django interviews, from a first backend role at an IT services firm in Pune to a senior position at a Bengaluru product company. It has 60 questions in three levels: easy questions on MVT, models, URLs, views, templates, forms, the admin and login; medium questions on the ORM, query optimisation, class-based views, middleware, signals, migrations, transactions, caching and Django REST Framework; and hard questions on row locking, query internals, zero-downtime migrations, async views, security, multi-tenant design and deployment. Answers follow Django 4.2 LTS and 5.x and name the version where behaviour changed. Read each answer, rebuild the code in a small project, then explain it aloud.
- 60 questions, 3 levels
- Updated
- CoreHRx Jobs
- 162 open Django jobs
What Django interviews cover
- Core framework: the MVT pattern, projects and apps, URL routing, function-based and class-based views, templates, forms and the admin.
- Models and the ORM: fields and relations, lazy QuerySets, lookups, F and Q objects, aggregation, and the N+1 problem with select_related and prefetch_related.
- Data safety: migrations in a team, data migrations, transactions, row locking with select_for_update, and transaction.on_commit for side effects.
- Django REST Framework: serializers and validation, viewsets and routers, authentication, permissions, throttling and pagination.
- Production topics: security (CSRF, XSS and SQL injection), caching, async views, WSGI and ASGI servers, static files and deployment settings.
How to prepare as a fresher or an experienced developer
- Freshers: build one small project end to end, such as a course enquiry or job board app, with models, a ModelForm, login, the admin and a few templates.
- Learn the ORM by watching the SQL: print str(qs.query) or use Django Debug Toolbar, and count how many queries each page runs.
- Add a small DRF API to the same project with a ModelSerializer, a ViewSet, session or token authentication and IsAuthenticated, and try it in the browsable API.
- Experienced candidates: prepare real stories, such as an N+1 you fixed, a migration you ran on a large table, a race you closed with select_for_update or F(), or a cache you had to invalidate.
- Find out which version the team uses. Django 4.2 and 5.2 are long-term support releases; name version-specific features by number, such as STORAGES in 4.2 or LoginRequiredMiddleware in 5.1.
Common mistakes and interview-day tips
- Saying the ORM makes SQL injection impossible; raw(), extra() and cursor.execute() with f-strings are still vulnerable.
- Forgetting that DRF allows every request by default until DEFAULT_PERMISSION_CLASSES is set, and that object permissions do not filter list endpoints.
- Doing slow work such as emails, SMS or PDF generation inside the request; move it to a background worker and queue it with transaction.on_commit.
- Calling get() in a view without handling DoesNotExist, or editing a migration that has already run in production.
- In a live coding round, write the model first, then the query, and say how many queries the page will run; interviewers notice candidates who think about the database.
Django interview questions: Easy questions (freshers)
20 questions1. What is Django, and what does the MVT pattern mean?
Django is a free, open-source Python web framework that ships with most of what a web app needs: an ORM, URL routing, templates, forms, an admin site, authentication and protection against common attacks. MVT stands for Model, View, Template. The model defines the data and talks to the database, the view receives a request and decides what to return, and the template turns data into HTML. The controller part of MVC is done by Django itself through the URL dispatcher. A common slip is calling the Django view a controller and the template a view; use Django's own names.
2. What is the difference between a Django project and an app?
A project is the whole website: its settings, the root URL configuration and the WSGI and ASGI entry points, created with django-admin startproject. An app is a package that does one job, such as jobs, payments or accounts, with its own models, views, templates and migrations, created with manage.py startapp. One project holds many apps, and a well-made app can be reused in another project. An app's models and migrations are picked up only after you add it to INSTALLED_APPS. A common fresher mistake is putting everything in one giant app; split by business area so each app stays small.
3. How do you define a model, and how does it map to a database table?
A model is a Python class that subclasses models.Model. Each field attribute becomes a column and each instance is one row. Django names the table app label plus model name, academy_course here, and adds an id primary key unless you declare one; new projects make it a BigAutoField through DEFAULT_AUTO_FIELD. The field type sets the column type and the validation: CharField takes max_length (optional only on PostgreSQL, since Django 4.2), and DecimalField needs max_digits and decimal_places. Use DecimalField, never FloatField, for money such as a course fee in rupees.
from django.db import models
class Course(models.Model):
title = models.CharField(max_length=200)
city = models.CharField(max_length=80)
fee = models.DecimalField(max_digits=10, decimal_places=2)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.title} ({self.city})"4. What do makemigrations and migrate do?
makemigrations compares your models with the existing migration files and writes a new migration describing the changes, such as a new field or table. It does not touch the database. migrate applies the migrations that have not run yet and records each one in the django_migrations table, so the same migration never runs twice. Commit migration files so every developer and server builds the same schema. showmigrations lists what is applied and sqlmigrate prints the SQL. A common mistake is changing a model and forgetting makemigrations, which leaves the code and the database out of step.
5. How does URL routing work in Django?
Django loads the module named in ROOT_URLCONF and checks its urlpatterns from top to bottom; the first pattern that matches calls its view. path() uses converters such as int, slug, str, uuid and path to capture parts of the URL and pass them to the view as keyword arguments. include() lets each app keep its own urls.py. Give every route a name, and set app_name for a namespace, so code and templates build URLs with reverse() or the url tag instead of hard-coding them. If nothing matches, Django returns a 404. Order matters: a broad pattern placed above a specific one catches its requests.
# hiring/urls.py
from django.urls import include, path
urlpatterns = [
path("courses/", include("academy.urls")),
]
# academy/urls.py
from django.urls import path
from . import views
app_name = "academy"
urlpatterns = [
path("", views.course_list, name="list"),
path("<int:pk>/", views.course_detail, name="detail"),
]6. What is a function-based view, and what must it return?
A function-based view is a plain Python function that takes an HttpRequest as its first argument, plus any values captured from the URL, and returns an HttpResponse. Most views use the render() shortcut, which loads a template, fills it with a context dictionary and returns the response; JsonResponse turns a dictionary into JSON for simple APIs. A view that returns None makes Django raise an error, a common bug when one if branch forgets its return. Keep views thin: read the input, call the model or a service function, and return the response. Query parameters arrive in request.GET.
from django.http import JsonResponse
from django.shortcuts import render
from .models import Course
def course_list(request):
courses = Course.objects.filter(is_active=True)
city = request.GET.get("city")
if city:
courses = courses.filter(city=city)
return render(request, "academy/course_list.html",
{"courses": courses})
def course_count(request):
n = Course.objects.filter(is_active=True).count()
return JsonResponse({"active": n})7. How do Django templates work, and what is template inheritance?
The Django template language mixes HTML with variables in double curly braces, tags such as for, if and url, and filters after a pipe that format values, such as date or default. Inheritance avoids repeating the layout: base.html defines named blocks, and a child template starts with the extends tag, which must be its first tag, and fills only the blocks it needs. The empty tag inside a for loop handles an empty list. Variables are escaped automatically, which protects against XSS. Keep heavy logic out of templates; calculations belong in the view or the model.
{# templates/base.html #}
<!doctype html>
<html>
<body>
<header>Skill Academy</header>
{% block content %}{% endblock %}
</body>
</html>
{# templates/academy/course_list.html #}
{% extends "base.html" %}
{% block content %}
{% for course in courses %}
<p>{{ course.title }} - {{ course.city|default:"Online" }}</p>
{% empty %}
<p>No courses yet.</p>
{% endfor %}
{% endblock %}8. What is a QuerySet, and how do all(), filter(), exclude() and get() differ?
A QuerySet is a lazy collection of rows, built through a model's manager, usually objects. all() returns every row, filter() keeps the rows that match, and exclude() removes them; each returns a new QuerySet, so you can chain them. Lookups use a double underscore, such as fee__lte or title__icontains. get() returns exactly one object: it raises DoesNotExist when no row matches and MultipleObjectsReturned when more than one does. Use get() on unique fields such as the primary key and handle the exception, or use first() when zero rows is a normal case.
from academy.models import Course
active = Course.objects.filter(is_active=True)
cheap_pune = active.filter(city="Pune", fee__lte=5000)
not_online = active.exclude(city="Online")
python = active.filter(title__icontains="python")
try:
course = Course.objects.get(pk=42)
except Course.DoesNotExist:
course = None9. How do you model a one-to-many relation, and what does on_delete do?
Put a ForeignKey on the many side: each Application points to one Job, and a job has many applications. on_delete is required and says what happens to the applications when their job is deleted: CASCADE deletes them too, PROTECT blocks the delete with ProtectedError, SET_NULL stores NULL (the field needs null=True), and SET_DEFAULT or SET() put another value in. related_name names the reverse accessor, so job.applications.all() reads better than the default job.application_set.all(). Choose PROTECT for records you must keep, such as payments.
from django.db import models
class Job(models.Model):
title = models.CharField(max_length=200)
class Application(models.Model):
job = models.ForeignKey(
Job, on_delete=models.CASCADE, related_name="applications"
)
candidate_name = models.CharField(max_length=120)
# job.applications.all() lists every application for one job10. What is the Django admin, and how do you customise it?
The admin is a back-office site generated from your models. Run createsuperuser, register a model in the app's admin.py, and staff users can list, search, add and edit its rows under /admin/. A ModelAdmin class shapes the screens: list_display picks the columns, list_filter adds sidebar filters, search_fields adds a search box and readonly_fields blocks edits. Only users with is_staff can sign in, and model permissions decide what each one may do. The admin is meant for internal staff, such as an operations team, not as the main screen for customers.
from django.contrib import admin
from .models import Course
@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
list_display = ("title", "city", "fee", "is_active")
list_filter = ("city", "is_active")
search_fields = ("title",)
readonly_fields = ("created_at",)11. What is the difference between a Form and a ModelForm?
A Form is a set of fields with validation that is not tied to a model, such as a contact or search form. A ModelForm builds its fields from a model and can save the cleaned data straight to it with save(). In both, you bind the submitted data, call is_valid(), and then read cleaned_data, which holds converted values such as dates and integers instead of raw strings. List the fields in Meta.fields explicitly: "__all__" can let a user set a field they should not, such as is_verified. When validation fails, render the same form again so it shows its errors.
from django import forms
from django.shortcuts import redirect, render
from .models import Enquiry
class EnquiryForm(forms.ModelForm):
class Meta:
model = Enquiry
fields = ["name", "phone", "course"]
def enquiry(request):
form = EnquiryForm(request.POST or None)
if request.method == "POST" and form.is_valid():
form.save()
return redirect("academy:thanks")
return render(request, "academy/enquiry.html", {"form": form})12. What is CSRF, and how does Django protect against it?
Cross-site request forgery is when another website makes a logged-in user's browser send a request to your site, such as a form that changes their bank details, and the browser attaches the user's cookies. Django's CsrfViewMiddleware rejects POST, PUT, PATCH and DELETE requests without a valid token and returns 403 Forbidden. In templates, put the csrf_token tag inside every POST form. For JavaScript calls, read the csrftoken cookie and send it in the X-CSRFToken header. Use csrf_exempt only for endpoints that check requests another way, such as a signed payment webhook.
13. Which settings in settings.py matter most, and why?
DEBUG must be False in production, because True shows detailed error pages with code and settings to anyone. SECRET_KEY is used for signing: session data, password-reset tokens and anything built on django.core.signing, so it must be secret, read from an environment variable and never committed. ALLOWED_HOSTS lists the domains the site answers to and must be set once DEBUG is off. DATABASES holds the connection details, INSTALLED_APPS lists the apps and MIDDLEWARE lists the middleware in order. Keep per-environment values in environment variables instead of editing the file on each server.
14. What is the difference between static files and media files?
Static files are the CSS, JavaScript and images that ship with your code; media files are uploaded by users at run time, such as resumes or profile photos. STATIC_URL is the URL prefix for static files, STATICFILES_DIRS lists extra source folders, and collectstatic copies everything into STATIC_ROOT for the web server. MEDIA_ROOT is where uploads are saved and MEDIA_URL is their prefix. runserver serves static files when DEBUG is True; in production nginx, WhiteNoise or a CDN does. Never trust an uploaded file's name or type, and never store uploads where the server can run them as code.
15. How do login and logout work with Django's built-in authentication?
django.contrib.auth gives a User model, password hashing and login views. authenticate() checks the credentials against the configured backends and returns the user or None. login() stores the user's id in the session and changes the session key to block session fixation, and logout() clears the session. The login_required decorator sends anonymous users to LOGIN_URL with a next parameter. Passwords are stored as salted hashes, PBKDF2 by default, so use set_password() and check_password() and never save a raw password. Most projects use the built-in LoginView instead of writing this by hand.
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
def sign_in(request):
if request.method == "POST":
user = authenticate(request,
username=request.POST.get("username"),
password=request.POST.get("password"))
if user is not None:
login(request, user)
return redirect("dashboard")
return render(request, "sign_in.html")
@login_required
def dashboard(request):
return render(request, "dashboard.html")16. What is middleware in Django? Name a few built-in ones.
Middleware is code that wraps every request and response. Each can change the request on the way in and the response on the way out, or return a response early. The MIDDLEWARE setting lists them in order: requests pass through top to bottom and responses return bottom to top. Built-in examples: SecurityMiddleware sets security headers, SessionMiddleware loads the session, CsrfViewMiddleware checks CSRF tokens, AuthenticationMiddleware sets request.user and XFrameOptionsMiddleware stops clickjacking. Order matters: authentication needs the session, so SessionMiddleware comes first.
17. What is Django REST Framework, and what does a serializer do?
Django REST Framework (DRF) is a library for building JSON APIs on Django, for example for a mobile app or a React front end. It adds serializers, API views and viewsets, routers, authentication, permissions, throttling, pagination and a browsable API. A serializer converts model instances into Python data that becomes JSON, and validates incoming data before you save it, much like a Django form. ModelSerializer builds its fields from a model. Pass many=True for a list, call is_valid() before save(), and read serializer.errors when validation fails.
from rest_framework import serializers
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Course
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = ["id", "title", "city", "fee"]
@api_view(["GET"])
def course_list(request):
courses = Course.objects.filter(is_active=True)
return Response(CourseSerializer(courses, many=True).data)18. What does get_object_or_404 do, and why is it better than calling get() directly in a view?
get_object_or_404 calls get() and turns a missing row into Http404, so the user sees a normal Not Found page. A plain get() raises DoesNotExist, which becomes a 500 server error if nobody catches it. It accepts a model, a manager or a QuerySet, which makes it a neat ownership check: pass a QuerySet already filtered to the logged-in user, and another user's id returns 404 instead of their data. That closes a common hole called IDOR, where changing a number in the URL shows someone else's record, such as another customer's order.
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render
from .models import Order
@login_required
def order_detail(request, pk):
order = get_object_or_404(
Order.objects.filter(customer=request.user), pk=pk
)
return render(request, "shop/order_detail.html",
{"order": order})19. What is the difference between null=True and blank=True on a model field?
null=True is about the database: the column may store NULL. blank=True is about validation: forms and the admin accept an empty value. They often go together, as on an optional date of joining. For text fields such as CharField and TextField, Django's convention is to avoid null=True and store an empty string, so there is only one way to say no value. The exception is an optional text field with unique=True, such as a PAN number, where NULL avoids clashes between empty strings. A common mistake is setting only null=True and then wondering why the form still says the field is required.
20. How do sessions work in Django?
SessionMiddleware gives each visitor a session: data on the server, linked to the browser by a sessionid cookie holding only a random key. By default the data lives in the django_session table; other backends use the cache, cache plus database, files or a signed cookie. You read and write request.session like a dictionary, for example to keep a cart. Values are serialised as JSON, so store ids and simple values, not model objects. The cookie lasts two weeks by default (SESSION_COOKIE_AGE). If you change a list inside the session in place, set request.session.modified to True so it is saved.
Ready to check yourself? Take the free Django mock test
Django interview questions: Medium questions (1-3 years)
20 questions21. QuerySets are lazy. When does Django actually run the query, and how does result caching work?
Building or chaining a QuerySet runs no SQL. The query runs when the QuerySet is evaluated: iterating over it, len(), list(), bool() or an if test, repr() in the shell, and slicing with a step. Once evaluated, it caches its rows, so a second loop over the same object reuses them, and count() then uses the cached length too. A method that returns a new QuerySet, such as filter(), starts with no cache and queries again. On an unevaluated QuerySet, count() and exists() run a cheap COUNT or LIMIT 1 query instead of loading rows, so use them when you do not need the objects.
qs = Course.objects.filter(city="Pune") # no SQL yet
qs = qs.exclude(fee__gt=20000) # still no SQL
titles = [c.title for c in qs] # SQL runs, rows are cached
fees = [c.fee for c in qs] # no SQL: uses the cache
total = qs.count() # no SQL: uses the cache
active = list(qs.filter(is_active=True)) # new QuerySet, new SQL
if Course.objects.filter(city="Goa").exists(): # LIMIT 1 query
print("Goa has courses")22. What is the N+1 query problem, and how do select_related and prefetch_related fix it?
N+1 happens when you load a list with one query and then touch a relation on each row, firing one extra query per row. Listing 50 jobs with their company and skills costs 1 + 50 + 50 = 101 queries. select_related fixes it for ForeignKey and OneToOne fields with a SQL JOIN, so the related row comes in the same query. prefetch_related is for ManyToMany and reverse ForeignKey relations: it runs one extra query per relation and joins the results in Python. The same page then needs 3 queries. Find N+1s with Django Debug Toolbar, and lock the count in tests with assertNumQueries.
# 1 + 50 + 50 queries
for job in Job.objects.all()[:50]:
print(job.company.name, [s.name for s in job.skills.all()])
# 3 queries in total
jobs = (Job.objects
.select_related("company")
.prefetch_related("skills")[:50])
for job in jobs:
print(job.company.name, [s.name for s in job.skills.all()])23. What is an F expression, and why use it to update a counter or stock?
F() refers to a column's value inside the database, so Django can write SQL such as stock = stock - 1 instead of reading the value into Python and saving it back. The read-modify-write version has a race: two requests both read 5 and both save 4, so one sale is lost. With F() the database does the arithmetic on the current value in one query. Add a filter such as stock__gt=0 and the update becomes a safe check-and-decrement: if update() returns 0, the item is sold out. If you assign an F() to an instance and save it, call refresh_from_db() to read the new number.
from django.db.models import F
# Race: two requests can both read 5 and both write 4
book = Book.objects.get(pk=7)
book.stock -= 1
book.save()
# Safe: the database decrements the current value
updated = (Book.objects
.filter(pk=7, stock__gt=0)
.update(stock=F("stock") - 1))
if updated == 0:
print("Sold out")24. When do you need Q objects, and how do you combine them?
Keyword arguments in one filter() call are joined with AND. Q objects let you build OR and NOT conditions: the pipe means OR, the ampersand AND and the tilde NOT, grouped with brackets like a boolean expression; since Django 4.1 the caret gives XOR. They suit search boxes and optional filters, because you can build the condition step by step in a loop. Positional Q arguments must come before keyword arguments in the same call. A common mistake is chaining two filter() calls and expecting OR; chaining only narrows the results further.
from django.db.models import Q
term = "python"
courses = Course.objects.filter(
Q(title__icontains=term) | Q(description__icontains=term),
~Q(city="Online"),
is_active=True,
)
# build the condition step by step
cond = Q()
for city in ["Pune", "Nagpur", "Nashik"]:
cond |= Q(city=city)
maharashtra = Course.objects.filter(cond)25. What is the difference between aggregate() and annotate()?
aggregate() summarises the whole QuerySet into a dictionary, such as total fees or the average payment. annotate() adds a calculated value to each object, such as the number of applications per job, and you can filter on it afterwards. Calling values() before annotate() groups by those fields, which gives a GROUP BY report such as jobs per city. Watch the joins: two Count() annotations over different relations in one query multiply the rows and inflate both numbers; use Count(..., distinct=True) or subqueries. aggregate() ends the chain, because it returns a dictionary, not a QuerySet.
from django.db.models import Avg, Count, Sum
totals = Payment.objects.aggregate(total=Sum("amount"),
avg=Avg("amount"))
# a dict with the keys "total" and "avg"
jobs = Job.objects.annotate(n_apps=Count("applications"))
popular = jobs.filter(n_apps__gte=10)
per_city = (Job.objects.values("city")
.annotate(n=Count("id"))
.order_by("-n"))26. When would you use class-based views instead of function-based views?
Class-based views shine for standard patterns: ListView, DetailView, CreateView, UpdateView and DeleteView already handle fetching, pagination, forms and redirects, and you override small methods such as get_queryset() or get_context_data(). Mixins such as LoginRequiredMixin add behaviour and must come first in the base list. Function-based views read top to bottom, which is clearer for one-off logic. A classic bug is a class attribute queryset filtered on date.today(): the value is fixed when the module loads, so the list goes stale. Put per-request filters in get_queryset().
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from .models import Application
class MyApplicationsView(LoginRequiredMixin, ListView):
template_name = "jobs/my_applications.html"
paginate_by = 20
def get_queryset(self):
return (Application.objects
.filter(candidate=self.request.user)
.select_related("job")
.order_by("-created_at"))
# urls.py: path("mine/", MyApplicationsView.as_view())27. How do you add custom validation to a Django form?
Validation runs in a fixed order: each field converts and checks its own value, then a method named clean_ plus the field name, such as clean_mobile(), runs, then the form's clean() runs for rules across fields. A field method must return the cleaned value or raise ValidationError. In clean(), call super().clean(), read cleaned_data with get() because a failed field is missing, and use add_error() to show the message beside the right field. On a ModelForm, calling super().clean() also keeps the model's uniqueness checks. Reusable rules, such as an Indian mobile format, belong in a validator.
import re
from django import forms
class BookingForm(forms.Form):
mobile = forms.CharField(max_length=10)
check_in = forms.DateField()
check_out = forms.DateField()
def clean_mobile(self):
m = self.cleaned_data["mobile"]
if not re.fullmatch(r"[6-9]\d{9}", m):
raise forms.ValidationError("Enter a 10-digit mobile.")
return m
def clean(self):
data = super().clean()
a, b = data.get("check_in"), data.get("check_out")
if a and b and b <= a:
self.add_error("check_out", "Must be after check-in.")
return data28. Why should a new project start with a custom user model, and how do you set one up?
Django's docs recommend a custom user model for every new project, even one like the default, because changing AUTH_USER_MODEL after the first migrate is painful: foreign keys and migrations all point at the user table. Subclass AbstractUser to keep username, password, groups and permissions and add fields such as a phone number; use AbstractBaseUser only for a different login, such as phone and OTP only. Set AUTH_USER_MODEL, for example accounts.User, before the first migration. In model fields use settings.AUTH_USER_MODEL, elsewhere get_user_model(); never import User directly.
29. How do permissions and groups work in Django?
For every model Django creates add, change, delete and view permissions, named like billing.change_invoice. Give them to groups, such as Accounts Team, not to users one by one, and check them with user.has_perm(), the permission_required decorator or PermissionRequiredMixin. Active superusers pass every check. Custom ones go in Meta.permissions, such as approve_invoice. The default backend has no object-level permissions: change_invoice means all invoices, so restrict rows yourself, for example by organisation. Permissions are cached on the user object; fetch the user again after granting one.
30. How do you write custom middleware, for example to log slow requests?
Middleware is a factory: it receives get_response once at startup and returns a callable that runs for every request. Code before get_response(request) runs on the way in, code after it runs on the way out, and returning a response early skips the view. Add its dotted path to MIDDLEWARE; near the top it times everything below it. Keep it fast and avoid database queries, because it runs on every request. For the process_view or process_exception hooks, write a class with __init__ and __call__ and add those methods. Log ids and timings, never passwords or tokens.
import logging
import time
log = logging.getLogger("perf")
def slow_request_logger(get_response):
def middleware(request):
start = time.monotonic()
response = get_response(request)
ms = (time.monotonic() - start) * 1000
if ms > 500:
log.warning("slow %s %s %.0f ms",
request.method, request.path, ms)
return response
return middleware
# settings.py: add "core.middleware.slow_request_logger"31. Walk through what happens when a request reaches a Django app.
The server hands the request to Django through WSGI or ASGI, and the handler builds an HttpRequest. It passes through each middleware in order, which may add request.session and request.user or return early. The URL resolver matches the path against ROOT_URLCONF and calls the view with the captured arguments. The view returns an HttpResponse, often by rendering a template. If it raises Http404 or PermissionDenied, Django turns that into a 404 or 403. The response then passes back through the middleware in reverse order, and at request_finished Django closes expired database connections.
32. What are signals, and when should you use them?
Signals let code react to events elsewhere without the sender knowing the receiver. Common ones are pre_save, post_save, pre_delete, post_delete and m2m_changed, plus request_started and request_finished. Connect receivers with the receiver decorator and import the module in your AppConfig.ready() so they register once. Receivers run synchronously, in the same thread and transaction as the save: a slow one slows the request and an exception reaches the caller of save(). Use them to decouple apps, such as creating a profile at sign-up; inside one app an explicit call is easier to test.
33. How do you validate and save data with a DRF ModelSerializer?
Pass data=request.data and call is_valid(raise_exception=True); in a DRF view a failure becomes a 400 listing the errors. Field rules go in a method such as validate_notice_days(self, value), which returns the value; rules across fields go in validate(self, attrs), which returns attrs. List server-controlled fields in read_only_fields, and pass values such as the current user to save(), which forwards them to create() or update(). ModelSerializer does not save nested data by default; for writable nested serializers you write create() and update() yourself.
class ApplicationSerializer(serializers.ModelSerializer):
class Meta:
model = Application
fields = ["id", "job", "notice_days", "status", "candidate"]
read_only_fields = ["status", "candidate"]
def validate_notice_days(self, value):
if value > 90:
raise serializers.ValidationError("At most 90 days.")
return value
def validate(self, attrs):
if not attrs["job"].is_open:
raise serializers.ValidationError("This job is closed.")
return attrs
# in the view
s = ApplicationSerializer(data=request.data)
s.is_valid(raise_exception=True)
s.save(candidate=request.user)34. Explain APIView, generic views and ViewSets with routers in DRF. When do you use each?
APIView is the base: you write get(), post() and so on yourself, with DRF's parsing, authentication and permissions. Generic views such as ListCreateAPIView and RetrieveUpdateDestroyAPIView add the common patterns once you set queryset and serializer_class. A ModelViewSet bundles list, create, retrieve, update, partial_update and destroy, and a router builds the URLs. The action decorator adds extra endpoints such as POST /jobs/5/close/. Use ViewSets for standard resources and APIView for unusual endpoints such as a payment callback. Set basename when the viewset has no queryset attribute.
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.routers import DefaultRouter
class JobViewSet(viewsets.ModelViewSet):
serializer_class = JobSerializer
def get_queryset(self):
return Job.objects.filter(company=self.request.user.company)
@action(detail=True, methods=["post"])
def close(self, request, pk=None):
job = self.get_object()
job.is_open = False
job.save(update_fields=["is_open"])
return Response({"status": "closed"})
router = DefaultRouter()
router.register("jobs", JobViewSet, basename="job")
urlpatterns = router.urls35. How do authentication and permissions work in DRF, and what is the most common security mistake?
Authentication identifies the caller; permissions decide what they may do. SessionAuthentication, TokenAuthentication or a JWT package set request.user; permission classes such as IsAuthenticated or IsAdminUser then allow or deny the request. The most common mistake is forgetting that DRF's default permission is AllowAny; set DEFAULT_PERMISSION_CLASSES. Next is relying on has_object_permission for lists: it runs only when a view calls get_object(), as in retrieve, update and destroy, so filter list results in get_queryset(). SessionAuthentication enforces CSRF for logged-in users.
36. How do you paginate and filter API results in DRF?
Set DEFAULT_PAGINATION_CLASS and PAGE_SIZE in REST_FRAMEWORK, or pagination_class on one view. PageNumberPagination gives ?page=2, LimitOffsetPagination gives ?limit=20&offset=40, and CursorPagination gives opaque next and previous links that stay stable while new rows arrive, which suits feeds but needs an ordering on an unchanging, nearly unique field such as a timestamp. SearchFilter adds ?search=, OrderingFilter adds ?ordering=, and the django-filter package adds field filters. Always paginate list endpoints, and set ordering_fields, or clients may sort on any readable serializer field.
37. How do you write a data migration, and why must it use apps.get_model()?
Create an empty migration with makemigrations --empty and add a RunPython operation with a forward function and, ideally, a reverse one. Inside it, get models with apps.get_model() instead of importing them: that returns the historical model as it was at that point in the migration history, so the migration still works after the real model later changes or loses a field. Historical models have no custom methods, and custom managers only when use_in_migrations is set. For big tables, update in batches. Pass RunPython.noop as the reverse when undoing needs no work.
from django.db import migrations
CITY_TO_STATE = {"Pune": "Maharashtra", "Indore": "Madhya Pradesh"}
def fill_state(apps, schema_editor):
Branch = apps.get_model("banking", "Branch")
for b in Branch.objects.filter(state="").iterator():
b.state = CITY_TO_STATE.get(b.city, "")
b.save(update_fields=["state"])
class Migration(migrations.Migration):
dependencies = [("banking", "0007_branch_state")]
operations = [
migrations.RunPython(fill_state, migrations.RunPython.noop),
]38. Two developers added migrations to the same app at the same time. What happens, and how do you fix it?
Both files depend on the same parent, say 0011, so the graph has two leaf nodes and migrate stops with a Conflicting migrations detected error. If the changes touch different fields, run makemigrations --merge, which writes a small merge migration depending on both. If they clash, one developer deletes their unapplied migration, pulls and runs makemigrations again. Never edit or delete a migration that has already run in production. Run makemigrations --check in CI so a missing migration fails the build; since Django 4.2 that check no longer writes the missing files.
39. How do database transactions work in Django, and what is transaction.on_commit for?
Django runs in autocommit mode: each query commits on its own. transaction.atomic, as a decorator or a with block, groups queries so they all commit together or all roll back if an exception leaves the block; nested blocks become savepoints. Do not catch a database error inside an atomic block and carry on; wrap the risky part in its own inner atomic block. ATOMIC_REQUESTS wraps each view in a transaction. Use transaction.on_commit() for side effects such as an SMS or a Celery task, so they run only after the commit and never for a rolled-back order.
from django.db import transaction
def place_order(user, cart):
with transaction.atomic():
order = Order.objects.create(customer=user,
total=cart.total)
for item in cart.items:
OrderLine.objects.create(order=order,
course=item.course,
price=item.price)
transaction.on_commit(
lambda: send_receipt_sms.delay(order.id))
return order40. What caching options does Django give you, and where would you use each?
Configure a backend in CACHES: Redis, built in since Django 4.0, or Memcached for production, and local memory only for development, because it is private to each process. The cache_page decorator caches a whole response, good for public pages such as jobs in one city. The cache template tag caches a fragment, such as a sidebar. The low-level API, cache.get(), cache.set() and cache.get_or_set(), caches any value, such as a slow report total. Every entry needs a timeout and a plan for invalidation: delete or version the key when the data changes, and never cache personal pages as public.
Ready to check yourself? Take the free Django mock test
Django interview questions: Hard questions (experienced)
20 questions41. Two requests debit the same wallet at the same moment. How do you prevent a double spend in Django?
Lock the row for the whole transaction with select_for_update() inside transaction.atomic(). The second request waits at its SELECT until the first commits, then reads the new balance, so it cannot spend money that is already gone. It must run inside atomic: evaluating it in autocommit raises TransactionManagementError, and SQLite ignores the lock, so test on PostgreSQL or MySQL. nowait=True raises instead of waiting, and skip_locked=True skips locked rows, which suits job queues. Lock rows in a fixed order to avoid deadlocks and keep the locked section short.
from decimal import Decimal
from django.db import transaction
class InsufficientFunds(Exception):
pass
def debit(wallet_id, amount: Decimal):
with transaction.atomic():
w = (Wallet.objects.select_for_update()
.get(pk=wallet_id))
if w.balance < amount:
raise InsufficientFunds()
w.balance -= amount
w.save(update_fields=["balance"])
LedgerEntry.objects.create(wallet=w, amount=-amount)42. A Django list page has become slow in production. How do you find and fix the cause?
Measure first. Locally, Django Debug Toolbar shows every query and its time; in tests, assertNumQueries pins the count; in production, use APM or the slow-query log, because connection.queries records only when DEBUG is True. The usual causes are N+1 queries, fixed with select_related and prefetch_related; wide rows, fixed with only() or values(); filters and sorts on unindexed columns, fixed with Meta.indexes; and deep OFFSET pages. QuerySet.explain() shows the plan. Beware only(): reading a deferred field later fires a query per object. In the admin, list_select_related helps.
43. How do Prefetch objects work, and what mistake makes prefetch_related useless?
Prefetch() controls the query used for a prefetch: pass a filtered or ordered queryset, and to_attr to store the result as a plain list on each object under a new name. The trap is calling filter(), exclude() or order_by() on the related manager inside a loop, such as job.applications.filter(status="shortlisted"): that builds a new query, ignores the prefetch cache, and the N+1 is back. all() reads the cache; filtering does not. Move the filter into a Prefetch instead. The Prefetch queryset can use select_related, so nested objects arrive in the same extra query.
from django.db.models import Prefetch
shortlisted = (Application.objects
.filter(status="shortlisted")
.select_related("candidate"))
jobs = Job.objects.prefetch_related(
Prefetch("applications", queryset=shortlisted,
to_attr="shortlisted_apps"))
for job in jobs: # 2 queries in total
for app in job.shortlisted_apps:
print(job.title, app.candidate.first_name)44. How do you use Subquery, OuterRef and Exists in the ORM?
They let one query refer to another row by row inside the database, instead of looping in Python. OuterRef("pk") points from the inner query to the current row of the outer one. Subquery() must return one value per row, so cut it to one column and one row with values() and [:1], for example each candidate's latest application date. Exists() returns a boolean and is often cheaper than a join plus distinct() for questions such as has this candidate applied in the last 30 days. Since Django 3.0 an Exists() can go straight into filter() without annotating first.
from datetime import timedelta
from django.db.models import Exists, OuterRef, Subquery
from django.utils import timezone
latest = (Application.objects
.filter(candidate=OuterRef("pk"))
.order_by("-created_at")
.values("created_at")[:1])
recent = Application.objects.filter(
candidate=OuterRef("pk"),
created_at__gte=timezone.now() - timedelta(days=30))
candidates = (Candidate.objects
.annotate(last_applied=Subquery(latest))
.filter(Exists(recent)))45. How do you design custom managers and QuerySet methods, and what are the pitfalls?
Put reusable filters on a QuerySet subclass and expose it with as_manager(), so calls chain in any order: Job.objects.open().in_city("Pune"). Methods on a plain Manager subclass cannot be called after filter(), which is why the QuerySet route is preferred. The first manager declared on a model is its default manager, used by the admin, dumpdata and many generic tools. If that manager hides rows, such as soft-deleted ones, they also vanish from the admin. Keep objects unfiltered and add a second manager for the filtered view, and name methods after the business rule so it lives in one place.
from django.db import models
from django.utils import timezone
class JobQuerySet(models.QuerySet):
def open(self):
return self.filter(is_open=True,
last_date__gte=timezone.localdate())
def in_city(self, city):
return self.filter(city__iexact=city)
class Job(models.Model):
title = models.CharField(max_length=200)
city = models.CharField(max_length=80)
is_open = models.BooleanField(default=True)
last_date = models.DateField()
objects = JobQuerySet.as_manager()
# Job.objects.open().in_city("Pune").order_by("-id")46. How do you run schema migrations on a large production table without downtime?
Treat the schema change and the code change as separate deploys, because old and new code run side by side during a rollout. To add a required column: add it as nullable, deploy code that writes it, backfill in batches outside one long transaction, then make it NOT NULL. To rename or drop a column, first deploy code that no longer uses it. Read the SQL with sqlmigrate before running it, since some operations rewrite or lock the whole table. On PostgreSQL, build indexes with AddIndexConcurrently in a migration with atomic = False, so writes are not blocked.
from django.contrib.postgres.operations import (
AddIndexConcurrently,
)
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [("payments", "0031_payment_utr")]
operations = [
AddIndexConcurrently(
"payment",
models.Index(fields=["utr"], name="payment_utr_idx"),
),
]47. How do async views work in Django 4.2 and 5.x, and when do they actually help?
Write the view with async def. Under ASGI the request can stay async end to end, while under WSGI each async view runs in its own one-off event loop, so the server gains no concurrency. Async pays off when a view waits on several slow network calls that asyncio.gather runs together. Since Django 4.1 the ORM has async methods such as aget(), acreate() and async for, but they still run the query in a thread through sync_to_async, so they do not run in parallel. Calling sync ORM code from async code raises SynchronousOnlyOperation, and transactions need a sync function wrapped in sync_to_async.
import asyncio
import httpx
from django.http import JsonResponse
async def verify_vendor(request, gstin):
async with httpx.AsyncClient(timeout=5) as client:
gst, bank = await asyncio.gather(
client.get(f"https://gst.example.com/v1/{gstin}"),
client.get(f"https://bank.example.com/v1/{gstin}"),
)
vendor = await Vendor.objects.aget(gstin=gstin)
return JsonResponse({"vendor": vendor.name,
"gst_ok": gst.status_code == 200,
"bank_ok": bank.status_code == 200})48. WSGI or ASGI: how do you choose, and how do you deploy each?
WSGI is the classic synchronous interface and the simple, proven choice for a normal Django site. Run it with Gunicorn behind nginx; with Gunicorn's default sync workers each process handles one request at a time, and its docs suggest two workers per CPU core plus one as a start. ASGI supports async views, streaming and long-lived connections; WebSockets also need the Channels package. Run it with Uvicorn, Daphne or Hypercorn. Pick ASGI for many slow outbound calls or real-time features, and WSGI otherwise. Either way, let nginx serve static files and terminate TLS.
49. What do you check before deploying a Django project to production?
Run manage.py check --deploy, which flags insecure settings. DEBUG must be False, SECRET_KEY must come from the environment and ALLOWED_HOSTS must list your domains. Behind HTTPS, turn on SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE and HSTS, starting with a small HSTS value. Since Django 4.0, CSRF_TRUSTED_ORIGINS entries need the scheme. Set SECURE_PROXY_SSL_HEADER only if your proxy sets that header and strips any value sent by clients. Configure LOGGING so errors reach alerts, set CONN_MAX_AGE per database, and run migrate and collectstatic in each release.
import os
DEBUG = False
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
ALLOWED_HOSTS = ["hr.example.com"]
CSRF_TRUSTED_ORIGINS = ["https://hr.example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 3600 # raise once HTTPS is proven
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["DB_NAME"],
"CONN_MAX_AGE": 60,
"CONN_HEALTH_CHECKS": True,
}
}50. How do you serve static files reliably in production, and what changed in Django 4.2?
Run collectstatic in every release to gather files into STATIC_ROOT, then let nginx, WhiteNoise or a CDN serve them with long cache headers. ManifestStaticFilesStorage adds a content hash to each name, such as app.3f9a1c2b7d4e.css, so browsers can cache them long-term and still fetch new files after a deploy; the static tag looks names up in staticfiles.json. If a file is missing from that manifest, the tag raises ValueError and the page returns 500. Django 4.2 added the STORAGES setting, replacing STATICFILES_STORAGE and DEFAULT_FILE_STORAGE, which Django 5.1 removed.
51. Django templates escape output automatically. How can XSS still happen in a Django app?
Auto-escaping covers variables printed into HTML text and quoted attributes, but several paths bypass it. The safe filter and mark_safe() switch escaping off, so user content passed through them runs as HTML; build HTML with format_html(), which escapes its arguments. Escaping does not protect a value in an unquoted attribute or a javascript: URL in href, so quote attributes and allow only http and https links. Never print a variable inside a script tag; pass data with the json_script filter. Rich text from users needs an allow-list HTML sanitiser such as nh3 before you store or show it.
from django.utils.html import format_html
from django.utils.safestring import mark_safe
# Unsafe: user text becomes live HTML
def badge_bad(name):
return mark_safe(f"<b>{name}</b>")
# Safe: the argument is escaped
def badge(name):
return format_html("<b>{}</b>", name)
{# template: hand data to JavaScript safely #}
{{ chart_data|json_script:"chart-data" }}
<script>
const data = JSON.parse(
document.getElementById("chart-data").textContent);
</script>52. The ORM prevents SQL injection, so when can a Django app still be vulnerable?
QuerySet methods send values as query parameters, so user input never becomes SQL. The risk comes back when SQL is written by hand: raw(), cursor.execute(), extra() and RawSQL. Building that SQL with f-strings, format() or the % operator puts the input straight into the query. Pass values in the params list instead, and do not wrap the %s placeholders in quotes. Parameters cannot carry identifiers such as a column name for ORDER BY, so map user choices through an allow-list. extra() is a last resort that Django no longer improves; prefer annotate() and database functions.
# Vulnerable: the input becomes part of the SQL
def search_bad(term):
return Employee.objects.raw(
f"SELECT * FROM hr_employee WHERE name = '{term}'")
# Safe: the driver sends term as a parameter
def search(term):
return Employee.objects.raw(
"SELECT * FROM hr_employee WHERE name = %s", [term])
SORTS = {"name": "name", "joined": "date_of_joining"}
def sorted_employees(key):
return Employee.objects.order_by(SORTS.get(key, "name"))53. How do you design caching for a busy Django site so users never see stale or someone else's data?
First decide what is safe to share. Public pages can use cache_page or the per-site cache, which needs UpdateCacheMiddleware first and FetchFromCacheMiddleware last in MIDDLEWARE. Personal pages must vary by user or not be cached; Django adds Vary: Cookie when the session is used, and vary_on_cookie makes it explicit. Put the tenant or user id in low-level keys. To invalidate, delete the key in transaction.on_commit after a write, or bump a version number in the key. When a hot key expires, prevent a stampede with a short lock or early refresh, and add jitter to timeouts.
import random
from django.core.cache import cache
from django.db import transaction
def headcount(org_id):
key = f"org:{org_id}:headcount"
return cache.get_or_set(
key,
lambda: Employee.objects.filter(org_id=org_id).count(),
timeout=600 + random.randint(0, 60))
def employee_changed(org_id):
transaction.on_commit(
lambda: cache.delete(f"org:{org_id}:headcount"))54. How would you design a multi-tenant SaaS in Django so one company can never see another's data?
The usual start is a shared database with an organisation foreign key on every tenant table. Make scoping the default, not something each view remembers: a base DRF viewset whose get_queryset() always filters by the user's organisation, perform_create() that sets it, and unique constraints that include it. Take the tenant from the authenticated user or the subdomain, never from a request field. DRF authenticates tokens inside the view, so Django middleware sees token callers as AnonymousUser. Test that tenant A gets 404 for tenant B's ids. Schema-per-tenant isolates more but slows migrations.
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
class OrgScopedViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
def get_queryset(self):
org = self.request.user.org
return super().get_queryset().filter(org=org)
def perform_create(self, serializer):
serializer.save(org=self.request.user.org)
class InvoiceViewSet(OrgScopedViewSet):
queryset = Invoice.objects.all()
serializer_class = InvoiceSerializer55. A DRF list endpoint is slow and fires hundreds of queries. How do you fix it?
Serializers hide queries: every nested serializer or related field reads a relation per row, and a SerializerMethodField that calls obj.applications.count() runs one query per object. Fix it in the view's get_queryset(): select_related for foreign keys used by nested serializers, prefetch_related for many-to-many and reverse relations, and annotate() for counts, which the serializer then reads as a plain field. Lock the count with assertNumQueries so it stays flat as rows grow. For large read-only exports, serializer overhead itself is high; values() with pagination or streaming is faster.
from django.db.models import Count
class JobSerializer(serializers.ModelSerializer):
company = CompanySerializer(read_only=True)
skills = serializers.SlugRelatedField(
many=True, read_only=True, slug_field="name")
applicant_count = serializers.IntegerField(read_only=True)
class Meta:
model = Job
fields = ["id", "title", "company", "skills",
"applicant_count"]
class JobViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = JobSerializer
def get_queryset(self):
return (Job.objects.select_related("company")
.prefetch_related("skills")
.annotate(applicant_count=Count("applications")))56. Session, token or JWT authentication for a DRF API: how do you choose, and how do you protect login endpoints?
For a web front end on the same site, SessionAuthentication is simple and safe, with CSRF checks. For mobile apps, DRF's TokenAuthentication keeps one token per user in the database: easy to revoke, but it never expires on its own. JWT, usually via djangorestframework-simplejwt, gives short-lived access and refresh tokens with no lookup per request, but an access token stays valid until it expires unless you keep a blocklist. Throttle login and OTP endpoints with ScopedRateThrottle on a shared cache such as Redis; DRF says throttling is not a security control, so add lockouts and alerts.
57. Which ORM operations skip save() and the save signals, and why does that matter?
QuerySet.update(), bulk_create() and bulk_update() write straight to the database: they do not call the model's save() and do not send pre_save or post_save. So any rule kept in save() or a post_save receiver, such as an audit entry or a cache clear, silently does not run. QuerySet.delete() is different: it skips each model's delete() method but still sends pre_delete and post_delete for every deleted row, including cascades, which is why it usually loads those rows first. Keep important rules in a service function that bulk paths also call, not only in save() or signals.
58. How do you use a read replica in Django, and what can go wrong?
Add the replica to DATABASES and a router class to DATABASE_ROUTERS. Its db_for_read() sends reads to the replica, db_for_write() sends writes to the primary, and allow_relation() and allow_migrate() control cross-database relations and where migrations run. using("replica") picks a database for one query. The main risk is replication lag: a user saves a form, is redirected, and the next page reads from a replica that has not caught up, so the change looks lost. Send reads that follow a write, and reads inside transactions, to the primary, and keep the replica for reports and heavy lists.
59. How do you process millions of rows in Django without running out of memory?
Looping over a QuerySet loads every row and keeps them in its cache. iterator() streams rows in chunks and skips the cache; on PostgreSQL it uses a server-side cursor, which you must turn off with DISABLE_SERVER_SIDE_CURSORS behind a PgBouncer in transaction pooling mode. Since Django 4.1, iterator() also applies prefetch_related when you pass chunk_size. Use values_list() when you need only a few columns, and write back in batches with bulk_update() or update(). For a big download, stream it with StreamingHttpResponse, or build the file in a background job and send a link.
import csv
import io
from django.http import StreamingHttpResponse
def csv_lines(rows):
buf = io.StringIO()
writer = csv.writer(buf)
for row in rows:
writer.writerow(row)
yield buf.getvalue()
buf.seek(0)
buf.truncate(0)
def export_payments(request):
rows = (Payment.objects.filter(org=request.user.org)
.values_list("id", "utr", "amount", "paid_on")
.iterator(chunk_size=2000))
resp = StreamingHttpResponse(csv_lines(rows),
content_type="text/csv")
resp["Content-Disposition"] = 'attachment; filename="pay.csv"'
return resp60. How do you test a Django app properly, including code that uses transactions and on_commit?
Use django.test.TestCase for most tests: each test runs inside a transaction that is rolled back, which is fast and isolated, and setUpTestData() builds shared data once per class. Because that transaction never commits, on_commit callbacks do not fire; wrap the call in captureOnCommitCallbacks(execute=True) to run them. Use TransactionTestCase only for real commits or locking, since it truncates tables and is slower. Test through the client with force_login(), lock query counts with assertNumQueries, and mock external services such as SMS gateways so tests never send real messages.
Practice questions for November 2026
New every month3 questions from the Django mock test, the way they appear in it. A new set comes on the 1st of every month. Pick your answer, then open it.
-
DEBUG is False and ALLOWED_HOSTS is empty. What happens when users open the site?
- Django falls back to DEBUG mode
- The site works for every host name automatically
- Django redirects to localhost
- Every request gets a 400 Bad Request
Show the answer
Answer: Every request gets a 400 Bad Request. With DEBUG off, Django checks the Host header of each request against ALLOWED_HOSTS and rejects unknown hosts with 400. This blocks Host header attacks; list your real domain names there.
-
Which middleware hook is called just before the view, with the view function and its arguments?
- process_response()
- __init__()
- process_view()
- process_request()
Show the answer
Answer: process_view(). process_view runs after URL resolution, so it knows which view will handle the request. It returns None to continue or an HttpResponse to skip the view, which suits checks based on the view itself.
-
Employee.manager is a nullable ForeignKey. What SQL does Employee.objects.select_related("manager") use?
- An INNER JOIN, dropping employees without a manager
- A LEFT OUTER JOIN, so employees without a manager are still returned
- A separate query per employee
- A subquery per row
Show the answer
Answer: A LEFT OUTER JOIN, so employees without a manager are still returned. For a nullable relation Django uses a LEFT OUTER JOIN so rows with NULL are kept and manager is None. For a non-null ForeignKey it can use an INNER JOIN, which gives the same rows.
Ready to apply?
There are 162 open jobs asking for Django on CoreHRx right now. Create a free profile once and apply with it.