Advanced APIs

  • Advanced Django REST Framework concepts for scalable API development.
  • What are Advanced APIs?

    Advanced APIs include features beyond basic CRUD, such as:

    • Pagination → Manage large datasets

    • Permissions → Control who can access APIs

    • Filtering, Ordering, Throttling (optional for later lessons)

    These are essential for real-world scalable APIs.

    Pagination

    What is Pagination?

    Pagination splits large datasets into smaller chunks, reducing:

    • Response size

    • Loading time

    • Server memory usage

    Example: Instead of sending 1000 records at once, send 10 per page

    DRF Built-in Pagination Classes

    Class

    Description

    PageNumberPagination

    Page number in URL (?page=2)

    LimitOffsetPagination

    Specify limit & offset (?limit=10&offset=20)

    CursorPagination

    Cursor-based, secure, efficient for large data

    Enable Pagination Globally

Global Pagination Setup

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 5
}
  • Description

    • All APIs now return 5 records per page by default

    • Client can request page: /api/students/?page=2

    Pagination in Specific API

Custom Pagination in APIView

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Student
from .serializers import StudentSerializer

class StudentListAPI(APIView):
    def get(self, request):
        paginator = PageNumberPagination()
        paginator.page_size = 3  # 3 records per page
        students = Student.objects.all()
        result_page = paginator.paginate_queryset(students, request)
        serializer = StudentSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)
  •  Description

    • Local pagination overrides global setting

    • get_paginated_response() → JSON with page info

Pagination Response Example

{
    "count": 12,
    "next": "http://127.0.0.1:8000/api/students/?page=2",
    "previous": null,
    "results": [
        {"id": 1, "name": "Rahul", "course": "Django"},
        {"id": 2, "name": "Amit", "course": "Python"},
        {"id": 3, "name": "Neha", "course": "React"}
    ]
}
  • Permissions

    What are Permissions?

    Permissions control who can access an API.

    • Works with authentication

    • Defines rules for GET, POST, PUT, DELETE

    DRF Built-in Permission Classes

    Permission

    Description

    AllowAny

    Everyone can access

    IsAuthenticated

    Only logged-in users

    IsAdminUser

    Only admin users

    IsAuthenticatedOrReadOnly

    Read access for all, write for authenticated

    Global Permission Setup

Global Permission

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}
  • Description

    • All APIs require authentication

    • Overrides per-view permission if specified

    Per-View Permission Example

View with IsAdminUser

from rest_framework.permissions import IsAdminUser
from rest_framework.views import APIView
from rest_framework.response import Response

class AdminOnlyAPI(APIView):
    permission_classes = [IsAdminUser]

    def get(self, request):
        return Response({"message": "Hello Admin"})
  • Description

    • Only admin users can access

    • Non-admin → 403 Forbidden

    Custom Permission Example

IsOwner Permission

from rest_framework.permissions import BasePermission

class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.user == request.user
  • Description

    • Checks if current user owns the object

    • Useful for user-specific data like profile or orders

Using Custom Permission in View

class UserProfileAPI(APIView):
    permission_classes = [IsOwner]

    def get(self, request, pk):
        profile = Profile.objects.get(pk=pk)
        self.check_object_permissions(request, profile)
        return Response({"username": profile.user.username})