Django Interview Questions 46-50 (Scenario-Based: Deployment, Auth, DRF)

Hello! This lesson is all about practical application. We've covered the theory of Django, DRF, and databases. Now, we'll walk through five common scenarios that you will absolutely encounter as a professional developer.

We'll cover how to implement JWT authentication, outline the steps for a production deployment on AWS, create a debugging checklist, fix the dreaded N+1 query problem, and build a custom middleware from scratch. These questions are designed to show an interviewer that you can not only talk about Django, but you can use it to solve real problems.

46. Scenario: Add JWT authentication to a Django REST API.

This is a very common task for modern web apps, especially those with separate JavaScript frontends or mobile apps. JWT (JSON Web Token) is a stateless authentication method.

Analogy: The Concert Wristband
Instead of a session (a coat-check ticket that the server has to look up in a database), JWT is like a tamper-proof wristband. The server gives it to you at login. It contains your `user_id` inside it, and it's cryptographically signed. To access a secure page, you just show the wristband. The server can verify the signature without ever looking in the database.

The most popular library for this is `djangorestframework-simplejwt`. Here is the step-by-step setup.

# 1. Install the library
# pip install djangorestframework-simplejwt

# 2. Add it to settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
    'rest_framework_simplejwt',
    ...
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # This tells DRF to use JWT for authentication
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    ...
}

# 3. Add the token URLs to my_project/urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    ...
    # This URL is where users POST their
    # username/password to get a token
    path('api/token/', 
         TokenObtainPairView.as_view(), 
         name='token_obtain_pair'),
         
    # This URL is used to get a new access token
    # using a valid refresh token
    path('api/token/refresh/', 
         TokenRefreshView.as_view(), 
         name='token_refresh'),
]

# 4. Secure your views
# Now, any DRF view that uses 'IsAuthenticated'
# will automatically require a valid JWT.

# my_app/views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated

class MySecureView(APIView):
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        # This code will only run if the user provides a
        # valid token in the 'Authorization: Bearer ...' header.
        return Response({'message': f'Hello, {request.user.username}!'})

47. Scenario: Deploy a Django project on AWS (EC2 + RDS + S3).

This is a high-level system design question. The interviewer wants to see that you understand the different components of a scalable, production deployment.

Analogy: The Restaurant Chain
You don't build your restaurant, kitchen, and warehouse all in one giant, flammable building. You use specialized services.

Here is the standard architecture:

  • 1. EC2 (Elastic Compute Cloud): The Restaurant
    This is your virtual server. It's the "customer-facing" part. It runs your actual application code. On this server, you would have:
    - NGINX: The maître d' at the front door. It handles HTTPS, serves static files (`/static/`), and passes all other requests to Gunicorn.
    - Gunicorn: The head waiter. It manages a pool of Python processes (your Django app) to handle multiple requests at once.
  • 2. RDS (Relational Database Service): The Bank Vault
    You do not run your own PostgreSQL/MySQL database on your EC2 instance. It's hard to manage, back up, and scale.
    - Solution: You use RDS, which is AWS's managed database service. AWS handles all the backups, security patching, and scaling for you. Your Django app (on EC2) connects to RDS over a private network.
  • 3. S3 (Simple Storage Service): The Warehouse
    You do not store user-uploaded files (media files) on your EC2 server's hard drive. Why? Because if the server crashes, all your user files are gone. And if you scale to two EC2 servers, one server won't have the files that were uploaded to the other.
    - Solution: You use S3. All user uploads go directly to this secure, infinitely scalable file storage. Your app just saves a reference to the S3 URL.

Deployment Checklist:
1. Set `DEBUG = False` and configure `SECRET_KEY` from environment variables.
2. Set up `django-storages` to point `MEDIA_ROOT` to your S3 bucket.
3. Run `python manage.py collectstatic` to gather all static files.
4. Upload these static files to an S3 bucket (or serve with NGINX).
5. Configure NGINX to serve static files and proxy other requests to Gunicorn.
6. Run Gunicorn: `gunicorn my_project.wsgi:application --bind 0.0.0.0:8000`.

48. Scenario: Debug a Django app not working in production.

This is a common "panic" scenario. The key is to have a calm, logical checklist. The problem is almost always a configuration or path issue.

Analogy: Your car won't start. You don't immediately rebuild the engine. You check the simple things first: "Is there gas?" "Is the battery connected?"

Here is the debugging checklist, from most to least likely:

  • 1. Check the Logs: This is step zero. The answer is almost always here.
    - Check the NGINX log: `/var/log/nginx/error.log` (Are you getting 502 Bad Gateway?)
    - Check the Gunicorn log: (e.g., `journalctl -u gunicorn`) (Is the Python app crashing?)
  • 2. Problem: 502 Bad Gateway
    - Meaning: NGINX is working, but it can't talk to Gunicorn.
    - Fix: Is Gunicorn running? (`systemctl status gunicorn`). Did you configure the NGINX proxy pass (e.g., `proxy_pass http://127.0.0.1:8000`) to match Gunicorn's bind address?
  • 3. Problem: 500 Server Error
    - Meaning: NGINX and Gunicorn are talking, but your Django app is crashing.
    - Fix: Check the Gunicorn logs. The Django traceback will be there. Common causes:
    - `DEBUG = False` but `ALLOWED_HOSTS` is not set correctly.
    - Failed database connection (wrong password in env variables).
    - `SECRET_KEY` not set.
  • 4. Problem: 404s for CSS/JS
    - Meaning: NGINX isn't finding your static files.
    - Fix: Did you run `collectstatic`? Does the `STATIC_ROOT` in your NGINX config (`alias /var/www/static/`) match the `STATIC_ROOT` in `settings.py`?

49. Scenario: Fix N+1 query issues using select_related / prefetch_related.

This is the exact same question as #21, but phrased as a practical scenario. This shows how important this one concept is.

The Scenario: You have a `ListView` for `Books`. The template shows each `Book`'s `Author` (a `ForeignKey`) and its `Toppings` (a `ManyToManyField`). The Django Debug Toolbar shows 102 queries for 10 books.

  • 1 query for 10 Books.
  • 10 queries for 10 Authors (N+1).
  • 10 queries for 10 sets of Toppings (N+1).
  • Total: 21 queries (if 10 books).

The Fix: You must use both `select_related` and `prefetch_related` to fetch all data in just three queries.

# models.py
class Author(models.Model):
    name = models.CharField(max_length=100)

class Topping(models.Model):
    name = models.CharField(max_length=50)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    toppings = models.ManyToManyField(Topping)

# views.py (The Fix)
class BookListView(ListView):
    model = Book
    
    def get_queryset(self):
        # 1. (Query 1) Get all Books, JOINING Author data
        #    because it's a ForeignKey
        qs = Book.objects.select_related('author')
        
        # 2. (Query 2 & 3) Do one extra lookup for all
        #    toppings and join them in Python.
        qs = qs.prefetch_related('toppings')
        
        return qs
# template.html
"""
{% for book in object_list %}
    <h3>{{ book.title }}</h3>
    
    <p>By {{ book.author.name }}</p> 
    
    <ul>
        {% for topping in book.toppings.all %} 
            <li>{{ topping.name }}</li>
        {% endfor %}
    </ul>
{% endfor %}
"""

50. Scenario: Create a custom middleware for request logging.

This tests your understanding of the request/response cycle. We want to log the `path`, `method`, and `user` for every single request that hits our server.

Middleware is a class (or function) that has access to the `request` before it hits the view, and the `response` after it leaves the view.

The simplest way is to create a class with an `__init__` and a `__call__` method.

# my_app/middleware.py
import logging

# Get a logger instance
logger = logging.getLogger(__name__)

class RequestLogMiddleware:
    def __init__(self, get_response):
        # This is boilerplate: run once on server start
        self.get_response = get_response

    def __call__(self, request):
        # --- Code here runs BEFORE the view ---
        
        # Get user, default to 'Anonymous'
        user = 'Anonymous'
        if request.user.is_authenticated:
            user = request.user.username

        # This is the "request" part of the log
        logger.info(
            f"Request: {request.method} {request.path} by {user}"
        )

        # This line is critical: it calls the next
        # middleware, or the view itself
        response = self.get_response(request)

        # --- Code here runs AFTER the view ---
        
        # This is the "response" part of the log
        logger.info(
            f"Response: {response.status_code} for {request.path}"
        )
        
        return response

# settings.py
# Finally, we must install our new middleware
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    # Add our custom middleware here.
    # Order can matter. Place it after auth/session
    # if you need access to request.user.
    'my_app.middleware.RequestLogMiddleware', 
    'django.middleware.common.CommonMiddleware',
    ...
]

Sample Output (in your server log):

INFO:my_app.middleware:Request: GET /admin/ by admin
INFO:my_app.middleware:Response: 200 for /admin/
INFO:my_app.middleware:Request: GET /api/products/ by Anonymous
INFO:my_app.middleware:Response: 200 for /api/products/
🚀 Deep Dive With AI Scholar