๐ฆ Using Clientele with Django REST Framework
This guide shows you how to scaffold a Python client for a Django REST Framework (DRF) API using Clientele and drf-spectacular.
๐ก Working Example: See a real Django REST Framework application with code examples from this guide in
server_examples/django_rest_framework/
Prerequisites
- A Django REST Framework application
- drf-spectacular installed and configured
Why drf-spectacular?
Django REST Framework doesn't include built-in OpenAPI schema generation. drf-spectacular is the recommended solution for generating OpenAPI 3.0 schemas from DRF applications. It's actively maintained and provides excellent OpenAPI support.
Step 1: Install and Configure drf-spectacular
If you haven't already set up drf-spectacular, follow these steps:
Install drf-spectacular
pip install drf-spectacular
Configure Django Settings
Add to your settings.py:
INSTALLED_APPS = [
# ...
'rest_framework',
'drf_spectacular',
# ...
]
REST_FRAMEWORK = {
# ...
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = {
'TITLE': 'Your API',
'DESCRIPTION': 'Your API description',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
}
Add URL Patterns
In your urls.py:
from drf_spectacular.views import SpectacularAPIView
from django.urls import path
urlpatterns = [
# ...
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
]
Step 2: Get Your OpenAPI Schema
After configuring drf-spectacular, your OpenAPI schema is available at:
http://your-api-domain/api/schema/
For local development:
http://localhost:8000/api/schema/
Downloading the Schema
You can either:
Option A: Use the URL directly (if the API is accessible):
clientele start-api -u http://localhost:8000/api/schema/ -o my_client/
Option B: Download the schema file first:
curl http://localhost:8000/api/schema/ > openapi.json
clientele start-api -f openapi.json -o my_client/
Option C: Generate schema file with Django management command:
python manage.py spectacular --file openapi.json
clientele start-api -f openapi.json -o my_client/
Step 3: Scaffold the Client
clientele start-api -u http://localhost:8000/api/schema/ -o my_client/
Async Client
If you want an async client (note: DRF itself is synchronous, but the client can be async):
clientele start-api -u http://localhost:8000/api/schema/ -o my_client/ --asyncio
Step 4: Use the scaffolded Client
Usage Example
from my_client import client, schemas
# List resources
response = client.list_users_api_users_get()
# Create a resource
user_data = schemas.UserRequest(
username="alice",
email="[email protected]"
)
response = client.create_user_api_users_post(data=user_data)
# Handle responses
match response:
case schemas.User():
print(f"User created: {response.username}")
case schemas.ValidationError():
print(f"Validation failed: {response.detail}")
Async usage Example
from my_async_client import client, schemas
async def create_user():
user_data = schemas.UserRequest(
username="alice",
email="[email protected]"
)
response = await client.create_user_api_users_post(data=user_data)
return response
Improving operationId in DRF
By default, drf-spectacular generates operation IDs from view names and paths. You can customize them for cleaner function names.
Using extend_schema Decorator
from drf_spectacular.utils import extend_schema
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@extend_schema(operation_id="list_users")
def list(self, request):
return super().list(request)
@extend_schema(operation_id="create_user")
def create(self, request):
return super().create(request)
@extend_schema(operation_id="get_user")
def retrieve(self, request, pk=None):
return super().retrieve(request, pk)
This generates:
client.list_users()instead of a generic operation IDclient.create_user()instead of verbose auto-generated namesclient.get_user()for clean, readable function names
For Function-Based Views
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import api_view
@extend_schema(
operation_id="get_user_stats",
responses={200: UserStatsSerializer}
)
@api_view(['GET'])
def user_stats(request, user_id):
# ...
return Response(data)
Authentication
See api authentication.
Serializers and Schemas
DRF serializers become Pydantic models in the generated client:
DRF Serializer
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'is_active']
Generated Pydantic Schema
# In my_client/schemas.py
import pydantic
class User(pydantic.BaseModel):
id: int
username: str
email: str
is_active: bool
Usage
# The client returns properly typed responses
response = client.get_user_api_users_id_get(id=123)
# response is typed as schemas.User
print(response.username)
Path and Query Parameters
Query parameters are automatically converted to function arguments:
DRF View with Filtering
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filterset_fields = ['is_active', 'username']
Client Usage
# Query parameters become function arguments
response = client.list_users_api_users_get(
is_active=True,
username="alice"
)
Response Models
DRF's serializers become response models in the client:
DRF ViewSet
from rest_framework import serializers, viewsets
class User(serializers.ModelSerializer):
class Meta:
model = UserModel
fields = ['id', 'username', 'email']
class UserViewSet(viewsets.ModelViewSet):
queryset = UserModel.objects.all()
serializer_class = User
Generated Client Usage
response = client.get_user_api_users_id_get(id=123)
# response is typed as schemas.User
print(response.username)
Best Practices
- Use
@extend_schemato provide clear operation IDs and response types - Keep schemas in sync by regenerating after API changes
- Version your generated client in git to track changes
- Test thoroughly after regenerating
- Document your serializers - drf-spectacular uses docstrings in schema generation