Python Django Rest Framework Drf Tutorial

Written by

in

Python Django REST Framework (DRF) tutorial – If you’re a Python developer looking to build robust, scalable APIs quickly, Django REST Framework (DRF) is the tool you’ve been waiting for. In this comprehensive guide we’ll walk you through everything from setting up a fresh Django project to mastering serializers, viewsets, routers, authentication, pagination, testing, and deployment. By the end of this tutorial you’ll have a production‑ready RESTful API that you can extend for any web or mobile application.

Why Choose Django REST Framework?

DRF sits on top of Django, leveraging its mature ORM, authentication system, and admin interface while adding powerful API‑specific features. Here are a few reasons why DRF is a top choice for Python developers:

  • Browsable API: Instantly explore your endpoints in a web browser.
  • Serialization: Convert complex Django models to JSON, XML, or other formats with minimal code.
  • Viewsets & Routers: Reduce boilerplate by automatically generating CRUD routes.
  • Authentication & Permissions: Built‑in support for token, session, OAuth, and custom schemes.
  • Extensible: Plug‑in pagination, throttling, and custom renderers as needed.

Getting Started: Project Setup

1. Install Django and DRF

python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install django djangorestframework

2. Create a New Django Project

django-admin startproject myapi
cd myapi
python manage.py startapp blog

3. Register Apps in settings.py

Add 'rest_framework' and your new app ('blog') to INSTALLED_APPS:

INSTALLED_APPS = [
    # Django defaults …
    'rest_framework',
    'blog',
]

4. Configure a Simple Global DRF Setting

For a quick start, enable the browsable API and set default pagination:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

Modeling Your Data

Let’s build a tiny blog API. First, define a Post model inside blog/models.py:

from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

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

    def __str__(self):
        return self.title

Run migrations:

python manage.py makemigrations
python manage.py migrate

Serializers: Turning Models into JSON

Serializers are the heart of DRF. They define how model instances are converted to native Python datatypes and then rendered as JSON.

Creating a PostSerializer

# blog/serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    author = serializers.ReadOnlyField(source='author.username')

    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author', 'created_at']

Views: Handling Requests

DRF offers several view styles. For a CRUD API, ModelViewSet is the most concise.

Define a PostViewSet

# blog/views.py
from rest_framework import viewsets, permissions
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all().order_by('-created_at')
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        # Automatically assign the logged‑in user as the author
        serializer.save(author=self.request.user)

Routing: Connecting URLs to Views

DRF’s DefaultRouter automatically creates the standard list, create, retrieve, update, and destroy routes.

Register the ViewSet

# myapi/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from blog.views import PostViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
    path('api-auth/', include('rest_framework.urls')),  # browsable API login
]

Authentication & Permissions

Secure your API with token authentication or JWT. Below is a quick setup for DRF’s built‑in token auth.

Install Token Auth

pip install djangorestframework-simplejwt

Add the authentication classes to settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
    # other settings …
}

Create Token Endpoints

# myapi/urls.py (add)
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

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

Now clients can obtain a JWT by POSTing {"username": "user", "password": "pass"} to /api/token/ and include the token in the Authorization: Bearer <token> header for protected endpoints.

Advanced Features

Custom Pagination

Sometimes you need cursor‑based pagination for large data sets. Define a custom paginator:

# blog/pagination.py
from rest_framework.pagination import CursorPagination

class PostCursorPagination(CursorPagination):
    page_size = 10
    ordering = '-created_at'

Then apply it to the viewset:

class PostViewSet(viewsets.ModelViewSet):
    # …
    pagination_class = PostCursorPagination

Filtering and Searching

DRF integrates with django-filter for powerful query‑parameter filtering.

pip install django-filter

Update settings.py and the viewset:

# settings.py
REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS'] = ['django_filters.rest_framework.DjangoFilterBackend',
                                            'rest_framework.filters.SearchFilter']

# blog/views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters

class PostViewSet(viewsets.ModelViewSet):
    # …
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ['author__username']
    search_fields = ['title', 'content']

Versioning Your API

Maintain backward compatibility by versioning URLs:

# myapi/urls.py
from rest_framework.versioning import URLPathVersioning

router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')

urlpatterns = [
    path('api/v1/', include((router.urls, 'v1'), namespace='v1')),
    # future versions can be added as /api/v2/ …
]

Testing Your API

DRF provides an APIClient that works like Django’s test client but handles JSON automatically.

Sample Test Case

# blog/tests.py
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth import get_user_model
from .models import Post

User = get_user_model()

class PostAPITests(APITestCase):
    def setUp(self):
        self.user = User.objects.create_user(username='tester', password='secret')
        self.client.login(username='tester', password='secret')
        self.post = Post.objects.create(
            title='First Post',
            content='Hello DRF!',
            author=self.user
        )

    def test_list_posts(self):
        url = reverse('v1:post-list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)

    def test_create_post(self):
        url = reverse('v1:post-list')
        data = {'title': 'New Post', 'content': 'Testing create'}
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Post.objects.count(), 2)

Deploying a DRF API to Production

  • Use a WSGI server: Gunicorn or uWSGI behind Nginx.
  • Static & Media Files: Serve via Nginx or a CDN.
  • Environment Variables: Keep secret keys and DB credentials out of source control.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *