Django REST Framework Interview Questions 36-40 (Auth, Throttling, Pagination, JWT)

Hello! Welcome to this advanced lesson on Django and Django REST Framework (DRF). In our last session, we introduced DRF and its core components: Serializers, ViewSets, and Routers.

Now, we'll focus on the professional features required to build a production-ready API. We'll cover how to secure your endpoints (Authentication and Permissions), how to protect your server from abuse (Throttling), how to handle large data sets (Pagination), and a popular way to handle authentication (JWT). We'll finish with a summary of all the key performance techniques in Django.

36. How do authentication and permissions work in DRF?

This is a critical concept. Just like in standard Django, DRF separates Authentication (who you are) from Permissions (what you're allowed to do).

Analogy: The Speakeasy
You go to a private club (an API endpoint):

  • Authentication: The bouncer at the door asks for your membership card (a Token) or your name (a Session). This step just identifies you as a valid member (`request.user`).
  • Permission: You go to the VIP room. The guard at the room's door checks your `request.user` against a list. Do you have a "VIP" flag (`is_staff`)? Are you the person who booked this room (`IsOwner`)? This step authorizes your specific action.

In DRF, you configure these as defaults in your `settings.py` or on a per-view basis.

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated, IsAdminUser

# Example of a secure view
class AdminOnlyView(APIView):
    # --- Authentication ---
    # "Which bouncers (methods) should we use
    # to identify the user?"
    authentication_classes = [TokenAuthentication]
    
    # --- Permissions ---
    # "After identifying the user, what rules must
    # they pass to get into *this* room?"
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get(self, request, format=None):
        # We only get here if the user provided a valid
        # token AND is an admin (is_staff=True).
        return Response({'secret_data': '...'})

# --- Example of a custom "owner-only" permission ---

from rest_framework.permissions import BasePermission

class IsOwnerOrReadOnly(BasePermission):
    """
    Allow any user to read (GET),
    but only the owner to write (POST, PUT, DELETE).
    """
    def has_object_permission(self, request, view, obj):
        # Read-only methods are always allowed
        if request.method in ('GET', 'HEAD', 'OPTIONS'):
            return True
            
        # Write permissions are only allowed if
        # the user *is* the object's owner
        return obj.owner == request.user

37. What are throttling and rate limiting in DRF?

Throttling (or Rate Limiting) is a feature in DRF that prevents abuse by limiting how many requests a user can make in a given period.

This is a crucial security and performance feature. It protects you from:

  • Malicious users trying to brute-force a password.
  • Scrapers hammering your API to steal all your data.
  • A single buggy client accidentally sending 1000 requests a second and crashing your server.

Analogy: The Bar
A bartender (the server) can only make so many drinks (requests) at once.

  • AnonymousUserRateThrottle: The bartender tells an unknown person at the door, "You look new. You can only have 10 drinks tonight." (Limits based on IP address).
  • UserRateThrottle: The bartender tells a regular member, "Hey Alice, you're a member, so you can have 100 drinks tonight." (Limits based on `user_id`).

You set this in your `settings.py`. DRF handles it automatically. If a user goes over the limit, DRF stops running your view and just sends back a `429 Too Many Requests` error.

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        # Limit anonymous users by IP
        'rest_framework.throttling.AnonRateThrottle',
        # Limit authenticated users by user_id
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        # 100 requests per day for anonymous users
        'anon': '100/day', 
        # 1000 requests per day for authenticated users
        'user': '1000/day'
    }
}

38. How does pagination work in DRF?

Pagination is the process of splitting a very large query result into smaller "pages."

If a user asks for `/api/products/`, you should never return all 10 million products in one go. This would crash your server and the user's browser.

Instead, you paginate the results. You return only 20 products (Page 1) and provide links for the user to get Page 2, Page 3, and so on.

DRF makes this trivial. You just add a pagination class to your `settings.py`.

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 
        'rest_framework.pagination.PageNumberPagination',
    # Show 20 items per page
    'PAGE_SIZE': 20 
}

Now, when a user makes a request to a `ListView` or `ViewSet`:

Request: `GET /api/products/`

Response (JSON):

{
    "count": 1023,
    "next": "http://api.example.com/api/products/?page=2",
    "previous": null,
    "results": [
        { "id": 1, "name": "Product 1" },
        { "id": 2, "name": "Product 2" },
        ...
        { "id": 20, "name": "Product 20" }
    ]
}

39. How do you implement JWT authentication in DRF?

JWT (JSON Web Token) is a popular stateless authentication method, especially for mobile apps and JavaScript frontends.

Analogy: The Concert Wristband

  • 1. Login: You go to the ticket booth (`/api/token/`) with your username and password.
  • 2. Get Token: The booth verifies you, gives you a tamper-proof wristband (the JWT), and sends you away. The booth doesn't need to remember you.
  • 3. Access: To get into the concert (a secure endpoint), you just show your wristband (the token) to the guard. The guard checks that the wristband is valid and not tampered with (checks the signature). They don't need to go back to the booth to check a list.

This is stateless because the server doesn't need to store the token in a database (unlike sessions). The token itself contains all the info (like `user_id`) in a way that is cryptographically signed.

You implement this in DRF using a third-party package, most commonly `djangorestframework-simplejwt`.

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

# 2. settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # Use JWT as the main authentication method
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    ...
}

# 3. my_project/urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    ...
    # This creates the '/api/token/' endpoint
    path('api/token/', TokenObtainPairView.as_view()),
    # This creates the '/api/token/refresh/' endpoint
    path('api/token/refresh/', TokenRefreshView.as_view()),
]

# 4. How the client uses it
#
# Client sends a POST to '/api/token/' with:
# { "username": "user", "password": "pw" }
#
# Server responds with:
# {
#   "refresh": "eyJ...long_refresh_token...eyJ",
#   "access": "eyJ...long_access_token...eyJ"
# }
#
# Client then sends the access token in the header
# for all future requests:
# "Authorization: Bearer eyJ...long_access_token...eyJ"

40. What are common performance optimization techniques in Django?

This is a great "big picture" question. A slow Django app is almost always slow in one of two places: the database or the template.

Here is a checklist of the most common optimizations, in order of importance:

  • 1. Optimize Database Queries (The N+1 Problem):
    This is the #1 problem. Use the Django Debug Toolbar to see your queries.
    - Use `select_related()` for `ForeignKey` and `OneToOneField`.
    - Use `prefetch_related()` for `ManyToManyField` and reverse `ForeignKey`.
  • 2. Implement Caching:
    - Cache the results of heavy, non-critical queries using the low-level cache API (`cache.set()`, `cache.get()`).
    - Cache entire pages that are the same for everyone using `@cache_page`.
    - Cache parts of a dynamic template using the {% cache %} template tag.
  • 3. Defer Heavy Work with Celery:
    Don't make a user wait. If a user signs up and you need to send a welcome email, don't do it in the view.
    - Solution: Use Celery to send that email asynchronously in a background task. The user gets an "OK" response instantly.
  • 4. Optimize the ORM:
    - Use `.values()` or `.values_list()` if you only need dictionary or list data, not full model objects.
    - Use `.defer()` and `.only()` to avoid selecting large text fields from the database if you don't need them.
    - Use `.iterator()` to process a very large queryset (e.g., 1 million rows) one by one, without loading them all into memory at once.
🚀 Deep Dive With AI Scholar