Django Interview Questions 41-45 (Deployment, Auth, Optimization, DRF Scenario)
Hello! We've covered a lot of Django and DRF theory in the previous lessons. Now it's time to put it all together. This lesson focuses on practical, scenario-based questions.
These are the "how-to" questions that test whether you can actually build and maintain a real-world application. We'll cover the high-level architecture of deployment, walk through common tasks like adding login and optimizing a query, and finish by building a mini-API from scratch. This is a great way to connect all the concepts we've learned.
41. How do you deploy a Django application (NGINX, Gunicorn, AWS, etc.)?
This is a high-level system design question. You should never use `python manage.py runserver` in production. That is a single-threaded, insecure server meant only for development.
A professional deployment has three main components:
Analogy: A High-End Restaurant
- 1. NGINX (The Web Server / Maître d'):
This is the "face" of your application. It sits at port 80/443 and greets all incoming requests. It's excellent at handling slow clients and serving static files.
- It serves all static files (CSS/JS) directly from the `STATIC_ROOT` folder (it's fast, Django is slow).
- It handles SSL/TLS termination (HTTPS).
- It "reverse proxies" all other requests to Gunicorn. - 2. Gunicorn (The App Server / Head Waiter):
This is a WSGI (Web Server Gateway Interface) server. It sits behind NGINX on an internal port (like 8000). Its job is to manage your actual Python application.
- It receives a request from NGINX.
- It spawns and manages a pool of worker processes (e.g., 4-8 workers) to run your Django code.
- It handles multiple concurrent requests and load-balances between your workers. - 3. Django (The Kitchen):
This is your application code itself, which Gunicorn calls. It receives the request from Gunicorn, runs your view, talks to the database, and returns a response.
Simplified Flow:
`User` → `NGINX` → `Gunicorn` → `Django`
What about AWS?
AWS (or Heroku, DigitalOcean, etc.) is the cloud provider that gives you the virtual servers (e.g., EC2 instances) to run NGINX and Gunicorn, plus the managed services for your database (e.g., AWS RDS for PostgreSQL) and file storage (e.g., AWS S3 for media files).
42. Scenario: Add login and logout functionality in Django.
This is a practical test of your knowledge of Django's "batteries-included" auth system. The key is to use Django's built-in forms and views.
Here are the 4 steps:
- 1. Configure URLs to use Django's built-in `LoginView` and `LogoutView`.
- 2. Create the `login.html` template.
- 3. Tell Django where to redirect after a successful login (in `settings.py`).
- 4. Add login/logout links to your base template.
# 1. my_project/urls.py
# Import the built-in auth views
from django.contrib.auth import views as auth_views
urlpatterns = [
...
# Django provides the view logic for us
path('login/', auth_views.LoginView.as_view(
template_name='registration/login.html'
), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
# 2. templates/registration/login.html
# Django looks for this specific path by default.
"""
<form method="post">
{% csrf_token %}
{{ form.as_p }} <button type="submit">Login</button>
</form>
"""
# 3. my_project/settings.py
# After login, Django will redirect the user to the homepage.
LOGIN_REDIRECT_URL = '/'
# After logout, it will also go to the homepage.
LOGOUT_REDIRECT_URL = '/'
# 4. templates/base.html (links)
"""
{% if user.is_authenticated %}
Hi {{ user.username }}!
<a href="{% url 'logout' %}">Log Out</a>
{% else %}
<a href="{% url 'login' %}">Log In</a>
{% endif %}
"""
Tip: The key is to show you don't need to write your own view or form. Django's `LoginView` automatically uses its built-in `AuthenticationForm` and handles all the logic of checking the password and setting the session cookie.
43. Scenario: Optimize a slow Django query.
This is the N+1 query problem, which we covered in a previous lesson. This is the most common and important optimization scenario.
The Scenario: You have a `ListView` for `Posts`, and in your template, you show the author's name for each post. The page is making 51 queries.
- - 1 query for the 50 posts.
- - 50 separate queries for each author.
The Fix: You must use `select_related` to fetch the related `Author` in the same query as the posts.
# models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
# --------------------------------------------------
# SLOW: The N+1 Problem
# --------------------------------------------------
# views.py (BAD)
class PostListView(ListView):
model = Post # This is just: Post.objects.all()
# template.html (BAD)
"""
{% for post in object_list %} <p>
{{ post.title }} by
{{ post.author.name }} </p>
{% endfor %}
"""
# --------------------------------------------------
# FAST: The Optimized Solution
# --------------------------------------------------
# views.py (GOOD)
class PostListView(ListView):
model = Post
def get_queryset(self):
# We override get_queryset to add the optimization.
# This tells the ORM to "follow the ForeignKey"
# and get the author data using a SQL JOIN.
return Post.objects.select_related('author')
# template.html (GOOD)
# The template code doesn't change at all!
"""
{% for post in object_list %}
<p>
{{ post.title }} by
{{ post.author.name }} </p>
{% endfor %}
"""
Key Takeaway: The fix is not in the template. The fix is in the view's queryset, where you use `select_related` (for `ForeignKey`/`OneToOne`) or `prefetch_related` (for `ManyToMany`/reverse `ForeignKey`) to fetch all the data in a minimal number of queries.
44. Scenario: Handle file upload in Django with validation.
This scenario combines knowledge of models, forms, and views. The key parts are:
- Using a `FileField` or `ImageField` on the model.
- Using a `ModelForm` to handle the data.
- Passing `request.FILES` to the form in the view.
- Adding `enctype="multipart/form-data"` to the HTML form.
For this scenario, let's add validation to only allow PDF files under 2MB.
# 1. my_app/models.py
class UserReport(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
report_file = models.FileField(upload_to='reports/')
# 2. my_app/forms.py
from django import forms
from .models import UserReport
class ReportForm(forms.ModelForm):
class Meta:
model = UserReport
fields = ['report_file']
# --- This is the validation step ---
def clean_report_file(self):
file = self.cleaned_data.get('report_file')
if file:
# Check file size (e.g., 2MB limit)
if file.size > 2 * 1024 * 1024:
raise forms.ValidationError("File is too large (max 2MB).")
# Check file type
if not file.name.endswith('.pdf'):
raise forms.ValidationError("Only PDF files are allowed.")
return file
# 3. my_app/views.py
def upload_report_view(request):
if request.method == 'POST':
form = ReportForm(request.POST, request.FILES)
if form.is_valid():
# We add the user manually before saving
report = form.save(commit=False)
report.user = request.user
report.save()
return redirect('dashboard')
else:
form = ReportForm()
return render(request, 'upload_report.html', {'form': form})
45. Scenario: Build a REST API for Employee Management using DRF.
This scenario tests if you can combine all the DRF concepts (Model, Serializer, ViewSet, Router) to build a complete CRUD (Create, Read, Update, Delete) API.
We will build a read-write API for an `Employee` model in just 4 steps.
# 1. models.py (The data structure)
from django.db import models
class Employee(models.Model):
name = models.CharField(max_length=100)
department = models.CharField(max_length=50)
salary = models.IntegerField()
def __str__(self):
return self.name
# 2. serializers.py (The data translator)
from rest_framework import serializers
from .models import Employee
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ['id', 'name', 'department', 'salary']
# 3. views.py (The logic)
from rest_framework import viewsets
from .models import Employee
from .serializers import EmployeeSerializer
class EmployeeViewSet(viewsets.ModelViewSet):
"""
This one class provides all CRUD endpoints:
- GET /employees/ (list)
- POST /employees/ (create)
- GET /employees/{id}/ (retrieve)
- PUT /employees/{id}/ (update)
- DELETE /employees/{id}/ (destroy)
"""
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
# We could add permissions here, e.g.:
# permission_classes = [permissions.IsAdminUser]
# 4. urls.py (The wiring)
from rest_framework.routers import DefaultRouter
from my_app import views
router = DefaultRouter()
router.register(r'employees', views.EmployeeViewSet)
# The API URLs are now available under /
urlpatterns = router.urls
By writing these four small blocks of code, you have created a fully functional, browsable API for your `Employee` model.