Table of Contents
- Prerequisites
- Setting Up the Project
- 2.1 Creating a Virtual Environment
- 2.2 Installing Dependencies
- 2.3 Initializing a Django Project
- Configuring Django Settings
- Creating a Data Model
- Serializers: Converting Data to JSON
- Building Views with ViewSets
- Configuring URLs
- Testing the API
- 8.1 Using the DRF Browsable API
- 8.2 Testing with curl or Postman
- Adding Authentication
- Implementing Pagination
- Advanced Features (Optional)
- 11.1 Filtering
- 11.2 Permissions
- Conclusion
- References
Prerequisites
Before starting, ensure you have the following installed:
- Python 3.8+: Download from python.org.
- pip: Python’s package installer (usually included with Python).
- Virtual Environment Tool:
venv(built-in) orvirtualenv(third-party).
Setting Up the Project
2.1 Creating a Virtual Environment
A virtual environment isolates project dependencies, preventing conflicts with other Python projects.
# Create a project folder
mkdir django-rest-api && cd django-rest-api
# Create and activate a virtual environment (Windows)
python -m venv venv
venv\Scripts\activate
# For macOS/Linux
python3 -m venv venv
source venv/bin/activate
You’ll know the environment is active when (venv) appears in your terminal prompt.
2.2 Installing Dependencies
Install Django and Django REST Framework (DRF) using pip:
pip install django==4.2.7 djangorestframework==3.14.0
- Django: The web framework that handles routing, ORM, and project structure.
- Django REST Framework: Extends Django to simplify API development with serializers, viewsets, and authentication.
2.3 Initializing a Django Project
Create a new Django project and an app to house your API logic:
# Create a Django project (replace "myproject" with your project name)
django-admin startproject myproject .
# Create an app (e.g., "blog_api" for a blog-themed API)
python manage.py startapp blog_api
- The
.indjango-admin startproject myproject .ensures the project files are created in the current directory (avoids nested folders). blog_apiis the app where we’ll define models, serializers, and views.
Configuring Django Settings
Django requires you to register apps and configure third-party tools in settings.py.
Open myproject/settings.py and update the INSTALLED_APPS list to include blog_api and rest_framework:
# myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog_api', # Our custom app
'rest_framework', # Django REST Framework
]
Creating a Data Model
APIs typically interact with a database. Let’s define a Post model to represent blog posts (title, content, author, etc.).
Open blog_api/models.py and add:
# blog_api/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE) # Link to Django's built-in User model
created_at = models.DateTimeField(auto_now_add=True) # Auto-set on creation
updated_at = models.DateTimeField(auto_now=True) # Auto-update on save
def __str__(self):
return self.title # Show title in admin interface
Explanation:
title: A short text field for the post title.content: A long text field for the post body.author: A foreign key linking to Django’sUsermodel (deletes posts if the author is deleted viaon_delete=models.CASCADE).created_at/updated_at: Timestamps for when the post was created/updated.
Apply Migrations
Django uses migrations to sync models with the database. Run these commands to create and apply migrations:
# Generate migration files based on the model
python manage.py makemigrations
# Apply migrations to the database
python manage.py migrate
You’ll see output confirming tables were created (e.g., blog_api_post for the Post model).
Serializers: Converting Data to JSON
APIs send and receive data in formats like JSON. Serializers convert Django model instances (Python objects) to JSON (and vice versa).
Create a serializers.py file in blog_api:
# blog_api/serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'created_at', 'updated_at']
read_only_fields = ['author', 'created_at', 'updated_at'] # Auto-set by server
def create(self, validated_data):
# Set the author to the current authenticated user
validated_data['author'] = self.context['request'].user
return super().create(validated_data)
Key Details:
ModelSerializer: Automatically generates fields and validation logic from thePostmodel.fields: Specifies which model fields to include in the API response.read_only_fields: Fields that cannot be modified via the API (e.g.,authoris set by the server).createmethod: Overrides the default to set theauthorto the authenticated user making the request.
Building Views with ViewSets
Views handle API requests (e.g., GET, POST). DRF’s ViewSets group related views (e.g., list, create, update) into a single class, reducing boilerplate.
Open blog_api/views.py and define a PostViewSet:
# blog_api/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().order_by('-created_at') # Fetch all posts, newest first
serializer_class = PostSerializer
permission_classes = [IsAuthenticatedOrReadOnly] # Allow unauthenticated users to read only
What is a ModelViewSet?
A ModelViewSet provides CRUD operations out of the box:
list():GET /posts/– Returns all posts.retrieve():GET /posts/{id}/– Returns a single post.create():POST /posts/– Creates a new post.update():PUT /posts/{id}/– Updates an existing post.partial_update():PATCH /posts/{id}/– Partially updates a post.destroy():DELETE /posts/{id}/– Deletes a post.
Permissions:
IsAuthenticatedOrReadOnly ensures:
- Authenticated users can create/update/delete posts.
- Unauthenticated users can only view posts.
Configuring URLs
URLs map API endpoints (e.g., /api/posts/) to views. DRF’s DefaultRouter automatically generates URLs for ViewSets.
Step 1: Create API URLs
Create blog_api/urls.py to define app-specific API routes:
# blog_api/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet) # Registers PostViewSet at /posts/
urlpatterns = [
path('', include(router.urls)), # Includes all router-generated URLs
]
Step 2: Link to Project URLs
Include the app’s API URLs in the project’s main urls.py:
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('blog_api.urls')), # API root at /api/
]
Now, your API will be available at:
GET /api/posts/– List all posts.POST /api/posts/– Create a post.GET /api/posts/{id}/– Retrieve a post.- And so on.
Testing the API
DRF provides a built-in browsable API for testing. Let’s start the development server and explore:
python manage.py runserver
Visit http://127.0.0.1:8000/api/posts/ in your browser. You’ll see DRF’s interactive interface:
Using the Browsable API
-
Create a Superuser: To test authenticated actions (e.g., creating posts), create a Django admin user:
python manage.py createsuperuserFollow the prompts to set a username, email, and password.
-
Log In: Visit
http://127.0.0.1:8000/api/posts/and click “Log in” in the top-right corner. Use your superuser credentials. -
Create a Post: Click “POST” and fill in the
titleandcontentfields. Theauthorwill auto-populate with your user. -
Test Endpoints: Use the interface to send
GET,PUT, orDELETErequests to test CRUD operations.
Testing with curl or Postman
For programmatic testing, use tools like curl or Postman:
Example: List Posts (GET)
curl http://127.0.0.1:8000/api/posts/
Example: Create a Post (POST)
curl -X POST http://127.0.0.1:8000/api/posts/ \
-H "Content-Type: application/json" \
-H "Authorization: Token YOUR_TOKEN" \
-d '{"title":"My First Post","content":"Hello, Django REST API!"}'
(We’ll cover authentication tokens in the next section.)
Adding Authentication
By default, anyone can create posts (if logged in via the browsable API). To secure the API for programmatic clients (e.g., mobile apps), add token authentication.
Step 1: Enable Token Authentication
Update myproject/settings.py to include DRF’s token authentication:
# myproject/settings.py
INSTALLED_APPS += ['rest_framework.authtoken'] # Add token auth app
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication', # Token-based auth
'rest_framework.authentication.SessionAuthentication', # For browsable API
],
}
Step 2: Generate Token URLs
Add token authentication endpoints to blog_api/urls.py:
# blog_api/urls.py
from rest_framework.authtoken.views import obtain_auth_token # Add this import
urlpatterns = [
path('', include(router.urls)),
path('token/', obtain_auth_token, name='api_token_auth'), # Token endpoint
]
Now, users can request a token by sending a POST to /api/token/ with their username and password.
Step 3: Get a Token
Use curl to request a token for your superuser:
curl -X POST http://127.0.0.1:8000/api/token/ \
-H "Content-Type: application/json" \
-d '{"username":"your_username","password":"your_password"}'
Response:
{"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}
Step 4: Use the Token in Requests
Include the token in the Authorization header for authenticated requests:
# Example: Create a post with a token
curl -X POST http://127.0.0.1:8000/api/posts/ \
-H "Content-Type: application/json" \
-H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
-d '{"title":"Token-Authenticated Post","content":"This post was created with a token!"}'
Implementing Pagination
As your API grows, returning all posts at once becomes inefficient. Add pagination to limit results per page.
Update myproject/settings.py to enable pagination:
# myproject/settings.py
REST_FRAMEWORK = {
# ... (previous auth settings)
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5, # Return 5 posts per page
}
Now, GET /api/posts/ will return:
{
"count": 10, # Total posts
"next": "http://127.0.0.1:8000/api/posts/?page=2", # Next page URL
"previous": null, # Previous page URL (null if first page)
"results": [ ... ] # 5 posts
}
Advanced Features (Optional)
11.1 Filtering
Allow users to filter posts by author, title, or date using django-filter:
pip install django-filter==22.1
Update settings.py and views.py:
# myproject/settings.py
INSTALLED_APPS += ['django_filters']
REST_FRAMEWORK = {
# ...
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}
# blog_api/views.py
from django_filters.rest_framework import DjangoFilterBackend # Add import
class PostViewSet(viewsets.ModelViewSet):
# ...
filter_backends = [DjangoFilterBackend]
filterset_fields = ['author__username', 'title'] # Filter by author username or title
Now users can filter posts with:
GET /api/posts/?author__username=johndoe
GET /api/posts/?title=Django
11.2 Permissions
Restrict post edits/deletions to the original author using IsAuthorOrReadOnly:
# blog_api/permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Allow read-only requests (GET, HEAD, OPTIONS)
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions only for the author
return obj.author == request.user
Update PostViewSet in views.py:
from .permissions import IsAuthorOrReadOnly # Add import
class PostViewSet(viewsets.ModelViewSet):
# ...
permission_classes = [IsAuthenticatedOrReadOnly, IsAuthorOrReadOnly]
Conclusion
You’ve built a fully functional REST API with Django and DRF! We covered:
- Project setup and configuration.
- Model creation and database migrations.
- Serializers for data conversion.
- ViewSets for CRUD operations.
- Authentication with tokens.
- Pagination and advanced features like filtering.
DRF’s flexibility lets you extend this API further with permissions, throttling, or custom serializers. Explore the official docs to dive deeper!