Authentication & Pagination

Difficulty: Advanced

DRF provides multiple authentication schemes, each suited for different use cases. SessionAuthentication uses Django's session framework and is ideal for browser-based clients. TokenAuthentication uses a token header and is suited for mobile and single-page applications. JWT (JSON Web Tokens) through third-party packages like djangorestframework-simplejwt is the most popular choice for modern SPAs and mobile apps.

TokenAuthentication works by generating a unique token for each user. The client includes this token in the Authorization header of every request: 'Authorization: Token abc123...'. DRF validates the token and identifies the user. Tokens can be created through a built-in view (obtain_auth_token) or programmatically.

Permissions control who can access API endpoints. DRF provides several built-in permission classes: AllowAny (no restrictions), IsAuthenticated (logged-in users only), IsAdminUser (staff users only), IsAuthenticatedOrReadOnly (anyone can read, only authenticated can write). Custom permissions extend BasePermission and implement has_permission() or has_object_permission().

Pagination controls how large result sets are divided into pages. PageNumberPagination uses ?page=2 style. LimitOffsetPagination uses ?limit=10&offset=20 style. CursorPagination uses opaque cursors for consistent ordering with real-time data. Configure pagination globally in REST_FRAMEWORK settings or per-view with pagination_class.

Filtering narrows down results based on query parameters. DRF supports filtering through django-filter (DjangoFilterBackend), search (SearchFilter), and ordering (OrderingFilter). These backends are added to filter_backends on ViewSets and generate query parameters like ?category=electronics&search=laptop&ordering=-price.

Throttling limits the rate of API requests to prevent abuse. DRF provides AnonRateThrottle (for unauthenticated users) and UserRateThrottle (for authenticated users). Rates are specified as 'number/period' like '100/hour' or '1000/day'. Custom throttles can implement more complex rate limiting logic.

Code examples

Token Authentication

# settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework.authtoken',  # Token auth app
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}

# urls.py
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    path('api/token/', obtain_auth_token, name='api-token'),
]

# After running migrate, obtain a token:
# POST /api/token/ {"username": "john", "password": "pass123"}
# Response: {"token": "abc123def456..."}

# Use the token in requests:
# GET /api/articles/
# Authorization: Token abc123def456...

Add rest_framework.authtoken to INSTALLED_APPS and run migrate to create the token table. Clients POST credentials to obtain a token, then include it in the Authorization header for authenticated requests.

Custom Permissions

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """Allow owners to edit, everyone else can only read."""

    def has_object_permission(self, request, view, obj):
        # Read permissions for any request (GET, HEAD, OPTIONS)
        if request.method in permissions.SAFE_METHODS:
            return True
        # Write permissions only for the owner
        return obj.author == request.user

class IsAdminOrReadOnly(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_staff

# Usage in ViewSet
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly]

has_permission() runs for all requests. has_object_permission() runs for single-object views. SAFE_METHODS includes GET, HEAD, OPTIONS. Multiple permission classes are ANDed together.

Pagination

# settings.py - Global pagination
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
}

# Custom pagination class
from rest_framework.pagination import PageNumberPagination

class LargeResultPagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = 'page_size'
    max_page_size = 100

# Per-view pagination
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = LargeResultPagination

# Response format:
# {
#   "count": 150,
#   "next": "http://api.example.com/articles/?page=2",
#   "previous": null,
#   "results": [{...}, {...}, ...]
# }

PageNumberPagination splits results into pages. page_size_query_param lets clients request custom page sizes. The response includes count, next, previous, and results. Set pagination_class per-view to override global settings.

Filtering, Search, and Ordering

# pip install django-filter

# settings.py
INSTALLED_APPS = ['django_filters']
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
}

# ViewSet with filtering
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['category', 'status', 'author']
    search_fields = ['title', 'content', 'author__username']
    ordering_fields = ['created_at', 'title', 'views']
    ordering = ['-created_at']  # Default ordering

# Example API calls:
# GET /api/articles/?category=1&status=published
# GET /api/articles/?search=django+tutorial
# GET /api/articles/?ordering=-views
# GET /api/articles/?category=1&search=django&ordering=-created_at

DjangoFilterBackend filters by exact field values. SearchFilter searches across text fields. OrderingFilter sorts results. All three can be combined in a single request. filterset_fields, search_fields, and ordering_fields control which fields are available.

Key points

Concepts covered

TokenAuth, JWT, Permissions, Pagination, Filtering, Throttling