Skip to main content

Django: Building REST APIs with Django REST Framework (DRF)

Django: Building REST APIs with Django REST Framework (DRF)

Django REST Framework (DRF) is a powerful toolkit for building RESTful APIs in Django, enabling developers to create scalable, secure, and maintainable web services. Integrated with Django’s Model-View-Template (MVT) architecture, DRF simplifies API development with features like serialization, authentication, and viewsets. This tutorial explores building REST APIs with DRF, covering setup, core components, and practical applications for creating APIs in applications like blogs, e-commerce platforms, or dashboards.


01. What Is Django REST Framework?

DRF extends Django to provide tools for building REST APIs, allowing clients (e.g., web, mobile) to interact with your application via HTTP methods (GET, POST, PUT, DELETE). It offers serializers for data conversion, views for handling requests, and authentication/permission systems for security. DRF is ideal for creating APIs for data-driven applications requiring CRUD (Create, Read, Update, Delete) operations.

Example: Basic DRF Setup

# Install DRF
pip install djangorestframework
# myproject/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # Add DRF
    'myapp',
]

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls')),
]

Output:

DRF installed and configured; API endpoints ready at http://127.0.0.1:8000/api/.

Explanation:

  • rest_framework - Adds DRF to the project.
  • URLs configured to route API requests to the app.

02. Key DRF Concepts

DRF provides a robust set of tools for API development. The table below summarizes key components and their roles:

Component Description Use Case
Serializers Convert data between Python objects and JSON Data validation, transformation
Views/Viewsets Handle HTTP requests and responses Define API endpoints
Authentication Verify user identity Secure API access
Permissions Control access to endpoints Restrict actions to authorized users
Routers Automatically map URLs to viewsets Simplify URL configuration


2.1 Creating a Model and Serializer

Example: Article Model and Serializer

# myapp/models.py
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

# myapp/serializers.py
from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author', 'created_at']
# Apply migrations
python manage.py makemigrations
python manage.py migrate

Output:

Article model and serializer ready for API operations.

Explanation:

  • ModelSerializer - Maps model fields to JSON.
  • Fields specify which data to include in the API.

2.2 Building API Views

Example: API View with Viewset

# myapp/views.py
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer
from rest_framework.permissions import IsAuthenticatedOrReadOnly

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]  # Read-only for unauthenticated users

# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

Output:

API endpoints available at http://127.0.0.1:8000/api/articles/.

Explanation:

  • ModelViewSet - Provides CRUD operations (list, retrieve, create, update, delete).
  • DefaultRouter - Automatically generates URL patterns.
  • IsAuthenticatedOrReadOnly - Allows read access to all, write access to authenticated users.

2.3 Authentication and Permissions

Example: Token Authentication

# Install DRF token authentication
pip install djangorestframework-simplejwt
# myproject/settings.py
INSTALLED_APPS = [
    'rest_framework',
    'rest_framework_simplejwt',
    # Other apps
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# myproject/urls.py
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path('api/', include('myapp.urls')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticated]  # Require authentication for all actions

Output:

JWT authentication enabled; token required for http://127.0.0.1:8000/api/articles/.

Explanation:

  • JWTAuthentication - Uses JSON Web Tokens for secure authentication.
  • IsAuthenticated - Restricts all API actions to authenticated users.
  • Token endpoints provide access and refresh tokens.

2.4 Filtering and Pagination

Example: Filtering and Pagination

# myproject/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}

# myapp/views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['author', 'created_at']  # Filter by author or date
# Install django-filter
pip install django-filter

Output:

Filtered and paginated results at http://127.0.0.1:8000/api/articles/?author=1.

Explanation:

  • PageNumberPagination - Limits results per page.
  • DjangoFilterBackend - Enables filtering by query parameters.

2.5 Incorrect API Implementation

Example: Unprotected API

# myapp/views.py (Incorrect)
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    # No permission_classes defined

Output:

Anyone can modify data at http://127.0.0.1:8000/api/articles/, risking security.

Explanation:

  • Omitting permission_classes exposes the API to unauthorized access.
  • Solution: Add IsAuthenticated or other permissions.

03. Effective Usage

3.1 Recommended Practices

  • Use viewsets for CRUD operations, secure APIs with authentication/permissions, and enable filtering/pagination for usability.

Example: Comprehensive DRF API

# myproject/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_simplejwt',
    'django_filters',
    'myapp',
]
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}

# myapp/models.py
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

# myapp/serializers.py
from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author', 'created_at']

# myapp/views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['author', 'created_at']

# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Output:

Secure, paginated, and filtered API at http://127.0.0.1:8000/api/articles/.
  • JWT authentication secures the API.
  • Pagination and filtering improve usability.
  • IsAuthenticatedOrReadOnly - Balances access control.

3.2 Practices to Avoid

  • Avoid unprotected endpoints or exposing sensitive data in serializers.

Example: Exposing Sensitive Data

# myapp/serializers.py (Incorrect)
from rest_framework import serializers
from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'password']  # Incorrect: Exposes password

Output:

Sensitive data (password) exposed in API responses.
  • Including sensitive fields risks data leaks.
  • Solution: Exclude sensitive fields or use read-only fields.

04. Common Use Cases

4.1 Blog API

Provide endpoints for managing blog posts with read-only access for unauthenticated users.

Example: Blog Post API

# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]

# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'posts', ArticleViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

Output:

Blog posts accessible at http://127.0.0.1:8000/api/posts/; write access requires authentication.

Explanation:

  • IsAuthenticatedOrReadOnly - Allows public reads, authenticated writes.
  • Viewset handles all CRUD operations.

4.2 User Profile API

Expose user profiles with secure authentication and filtering.

Example: User Profile API

# myapp/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email']  # Safe fields only

# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from django.contrib.auth.models import User
from .serializers import UserSerializer

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet

router = DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

Output:

User profiles accessible at http://127.0.0.1:8000/api/users/ with JWT token.

Explanation:

  • ReadOnlyModelViewSet - Limits to GET requests.
  • Excludes sensitive fields for security.

Conclusion

Django REST Framework, integrated with Django’s Model-View-Template architecture, provides a powerful platform for building REST APIs. Key takeaways:

  • Use serializers and viewsets for efficient data handling and CRUD operations.
  • Secure APIs with JWT authentication and permission classes.
  • Enhance usability with pagination and filtering.
  • Avoid exposing sensitive data or leaving endpoints unprotected.

With DRF, you can build scalable, secure, and user-friendly APIs for modern web applications!

Comments