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
Post a Comment