Django REST Framework - Modul 6
Von OpenAPI Schema bis interaktive Swagger UI
Standard zur Beschreibung von REST APIs (früher: Swagger Specification)
Beispiel:
{
"openapi": "3.0.0",
"info": {
"title": "Movie API",
"version": "1.0.0"
},
"paths": {
"/api/movies/": {
"get": { ... },
"post": { ... }
}
}
}
Demo:
http://localhost:8000/api/schema/swagger-ui/
✅ API Endpoints anzeigen
✅ Request/Response sehen
✅ Direkt testen (Try it out)
✅ Authentifizierung testen
Demo:
http://localhost:8000/api/schema/redoc/
✅ Übersichtliche Doku
✅ Suchfunktion
✅ Code-Beispiele
✅ Print-freundlich
DRF API Code
↓
OpenAPI Schema Generator (drf-spectacular)
↓
schema.json / schema.yaml
↓
Swagger UI / ReDoc (Visualisierung)
# Entwickler fragt:
"Welche Endpoints gibt es?"
"Welche Parameter braucht POST /movies/?"
"Was ist das Response-Format?"
"Wie authentifiziere ich mich?"
→ Viel Zeit verschwendet!
→ Fehler durch Missverständnisse
→ Immer Backend-Devs fragen
# Entwickler öffnet:
http://localhost:8000/api/schema/swagger-ui/
→ Sieht alle Endpoints
→ Sieht Request/Response Schemas
→ Testet direkt in Browser
→ Authentication mit Token
→ Selbstständig arbeiten! 🚀
# DRF hat CoreAPI (deprecated)
# → Veraltet
# → OpenAPI 2.0 (alt)
# → Limitierte Features
# → Nicht mehr maintained
# ⚠️ NICHT verwenden!
# Modern, aktiv maintained
# → OpenAPI 3.0/3.1
# → Swagger UI + ReDoc
# → Excellent DRF Integration
# → Customizable
# → Auto-generation
# → Type Hints Support
# ⭐ Empfohlen!
1. Analysiert DRF Code:
- ViewSets
- Serializers
- Permissions
- Authentication
2. Generiert OpenAPI Schema:
- Endpoints
- Request/Response Formats
- Authentication Methods
- Validation Rules
3. Stellt UI bereit:
- Swagger UI (interaktiv)
- ReDoc (lesbar)
# Terminal:
pip install drf-spectacular
# Version prüfen:
pip show drf-spectacular
# Output:
Name: drf-spectacular
Version: 0.27.0
Summary: Sane and flexible OpenAPI 3 schema generation for Django REST framework
# filepath: movieapi/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# DRF
'rest_framework',
'rest_framework.authtoken',
# DRF Spectacular (OpenAPI)
'drf_spectacular', # ← Hinzufügen!
# Unsere App
'movies',
]
# filepath: movieapi/settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
# Schema Generation mit drf-spectacular
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', # ← Wichtig!
}
# filepath: movieapi/settings.py
SPECTACULAR_SETTINGS = {
# Basis-Info
'TITLE': 'Movie API',
'DESCRIPTION': 'API für Film-Verwaltung mit Künstlern und Besetzungen',
'VERSION': '1.0.0',
'CONTACT': {
'name': 'API Support',
'email': 'support@movieapi.com',
'url': 'https://movieapi.com/support',
},
'LICENSE': {
'name': 'MIT License',
'url': 'https://opensource.org/licenses/MIT',
},
# Swagger UI Settings
'SERVE_INCLUDE_SCHEMA': False, # Schema-Endpoint nicht in UI anzeigen
'SWAGGER_UI_SETTINGS': {
'deepLinking': True, # Deep Linking aktivieren
'persistAuthorization': True, # Auth persistent (Token bleibt)
'displayOperationId': False, # Operation IDs ausblenden
'filter': True, # Filter/Suche aktivieren
},
# Schema-Generierung
'COMPONENT_SPLIT_REQUEST': True, # Request/Response getrennt
'SCHEMA_PATH_PREFIX': r'/api/', # URL-Prefix für API
# Security Schemes (Authentication)
'SECURITY': [
{
'tokenAuth': [], # Token Authentication
},
],
# Enum Settings
'ENUM_NAME_OVERRIDES': {
'ValidationErrorEnum': 'drf_spectacular.serializers.ValidationErrorEnum.choices',
},
# Preprocessing (Custom Hooks)
'PREPROCESSING_HOOKS': [
'drf_spectacular.hooks.preprocess_exclude_path_format',
],
# Postprocessing
'POSTPROCESSING_HOOKS': [
'drf_spectacular.hooks.postprocess_schema_enums',
],
# Server-URLs (für verschiedene Environments)
'SERVERS': [
{
'url': 'http://localhost:8000',
'description': 'Development Server',
},
{
'url': 'https://api.movieapi.com',
'description': 'Production Server',
},
],
}
# filepath: movieapi/urls.py
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
SpectacularAPIView, # OpenAPI Schema (JSON/YAML)
SpectacularSwaggerView, # Swagger UI
SpectacularRedocView, # ReDoc
)
urlpatterns = [
# Admin
path('admin/', admin.site.urls),
# API
path('api/', include('movies.urls')),
# DRF Auth (Browsable API Login)
path('api-auth/', include('rest_framework.urls')),
# OpenAPI Schema Endpoints
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
# → Download: schema.json oder schema.yaml
# → http://localhost:8000/api/schema/
# → http://localhost:8000/api/schema/?format=yaml
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
# → Swagger UI (interaktiv)
# → http://localhost:8000/api/schema/swagger-ui/
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
# → ReDoc (schöne Doku)
# → http://localhost:8000/api/schema/redoc/
]
/api/schema/ → OpenAPI Schema (JSON/YAML Download)/api/schema/swagger-ui/ → Interaktive Swagger UI (API testen)/api/schema/redoc/ → Schöne ReDoc Dokumentation (read-only)# Terminal:
python manage.py runserver
# Browser öffnen:
# 1. Swagger UI:
http://localhost:8000/api/schema/swagger-ui/
# 2. ReDoc:
http://localhost:8000/api/schema/redoc/
# 3. Schema (JSON):
http://localhost:8000/api/schema/
# 4. Schema (YAML):
http://localhost:8000/api/schema/?format=yaml
Swagger UI:
Movie API 1.0.0
API für Film-Verwaltung mit Künstlern und Besetzungen
Servers:
▼ http://localhost:8000 Development Server
Default
GET /api/movies/ List movies
POST /api/movies/ Create movie
GET /api/movies/{id}/ Retrieve movie
PUT /api/movies/{id}/ Update movie
PATCH /api/movies/{id}/ Partial update movie
DELETE /api/movies/{id}/ Delete movie
GET /api/artists/ List artists
POST /api/artists/ Create artist
...
Schemas
Movie
Artist
MovieCasting
# filepath: movies/serializers.py
from rest_framework import serializers
from .models import Movie, Artist, MovieCasting
class MovieSerializer(serializers.ModelSerializer):
"""
Serializer für Movie CRUD Operationen.
Verwaltet Film-Informationen inkl. Titel, Jahr, Genre, Bewertung und Beschreibung.
"""
class Meta:
model = Movie
fields = '__all__'
read_only_fields = ['id', 'created_at', 'updated_at']
# Field-Level Documentation
title = serializers.CharField(
max_length=200,
help_text="Titel des Films (max. 200 Zeichen)"
)
year = serializers.IntegerField(
help_text="Erscheinungsjahr des Films (z.B. 2010)"
)
genre = serializers.CharField(
max_length=100,
required=False,
allow_blank=True,
help_text="Genre des Films (z.B. 'Sci-Fi', 'Drama')"
)
rating = serializers.DecimalField(
max_digits=3,
decimal_places=1,
required=False,
allow_null=True,
help_text="Bewertung des Films von 0.0 bis 10.0"
)
description = serializers.CharField(
required=False,
allow_blank=True,
help_text="Beschreibung/Zusammenfassung des Films"
)
class ArtistSerializer(serializers.ModelSerializer):
"""
Serializer für Artist CRUD Operationen.
Verwaltet Künstler/Schauspieler-Informationen.
"""
full_name = serializers.CharField(source='full_name', read_only=True, help_text="Vollständiger Name (Vorname + Nachname)")
class Meta:
model = Artist
fields = '__all__'
read_only_fields = ['id', 'created_at', 'updated_at', 'full_name']
class MovieCastingSerializer(serializers.ModelSerializer):
"""
Serializer für MovieCasting (Besetzung).
Verknüpft Künstler mit Filmen und deren Rollen.
"""
artist_name = serializers.CharField(source='artist.full_name', read_only=True, help_text="Name des Künstlers")
movie_title = serializers.CharField(source='movie.title', read_only=True, help_text="Titel des Films")
class Meta:
model = MovieCasting
fields = '__all__'
read_only_fields = ['id', 'created_at', 'artist_name', 'movie_title']
role_name = serializers.CharField(
max_length=200,
help_text="Name der Rolle im Film (z.B. 'Neo', 'Trinity')"
)
is_main_role = serializers.BooleanField(
default=False,
help_text="Ist dies eine Hauptrolle? (True/False)"
)
order = serializers.IntegerField(
default=0,
help_text="Reihenfolge der Besetzung (0 = erste Rolle)"
)
# filepath: movies/views.py
import logging
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes
from .models import Movie, Artist, MovieCasting
from .serializers import MovieSerializer, ArtistSerializer, MovieCastingSerializer
logger = logging.getLogger('movies')
@extend_schema_view(
list=extend_schema(
summary="Liste aller Filme",
description="Gibt eine paginierte Liste aller Filme zurück.",
tags=['Movies'],
),
retrieve=extend_schema(
summary="Film-Details",
description="Gibt Details eines einzelnen Films zurück.",
tags=['Movies'],
),
create=extend_schema(
summary="Film erstellen",
description="Erstellt einen neuen Film. Authentifizierung erforderlich.",
tags=['Movies'],
examples=[
OpenApiExample(
'Inception Example',
value={
'title': 'Inception',
'year': 2010,
'genre': 'Sci-Fi',
'rating': 8.8,
'description': 'A thief who steals corporate secrets through dream-sharing technology.',
},
request_only=True,
),
],
),
update=extend_schema(
summary="Film aktualisieren",
description="Aktualisiert alle Felder eines Films. Authentifizierung erforderlich.",
tags=['Movies'],
),
partial_update=extend_schema(
summary="Film teilweise aktualisieren",
description="Aktualisiert einzelne Felder eines Films. Authentifizierung erforderlich.",
tags=['Movies'],
),
destroy=extend_schema(
summary="Film löschen",
description="Löscht einen Film. Authentifizierung erforderlich.",
tags=['Movies'],
),
)
class MovieViewSet(viewsets.ModelViewSet):
"""
ViewSet für Movie CRUD Operationen.
Bietet vollständige CRUD-Funktionalität für Filme:
- List: Alle Filme auflisten
- Retrieve: Einzelnen Film abrufen
- Create: Neuen Film erstellen
- Update: Film aktualisieren
- Delete: Film löschen
"""
queryset = Movie.objects.all()
serializer_class = MovieSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
@extend_schema(
summary="Top-bewertete Filme",
description="Gibt die Top 10 Filme mit Bewertung >= 8.0 zurück.",
tags=['Movies'],
parameters=[
OpenApiParameter(
name='min_rating',
type=OpenApiTypes.FLOAT,
location=OpenApiParameter.QUERY,
description='Minimale Bewertung (Standard: 8.0)',
required=False,
),
OpenApiParameter(
name='limit',
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
description='Anzahl der Ergebnisse (Standard: 10)',
required=False,
),
],
responses={200: MovieSerializer(many=True)},
)
@action(detail=False, methods=['get'])
def top_rated(self, request):
"""Top-rated Movies - Custom Action"""
min_rating = float(request.query_params.get('min_rating', 8.0))
limit = int(request.query_params.get('limit', 10))
logger.info("Top-rated movies requested: min_rating=%s, limit=%s", min_rating, limit)
movies = Movie.objects.filter(rating__gte=min_rating).order_by('-rating')[:limit]
serializer = self.get_serializer(movies, many=True)
return Response(serializer.data)
@extend_schema(
summary="Filme nach Genre",
description="Filtert Filme nach Genre.",
tags=['Movies'],
parameters=[
OpenApiParameter(
name='genre',
type=OpenApiTypes.STR,
location=OpenApiParameter.QUERY,
description='Genre-Name (z.B. "Sci-Fi", "Drama")',
required=True,
),
],
responses={200: MovieSerializer(many=True)},
)
@action(detail=False, methods=['get'])
def by_genre(self, request):
"""Filme nach Genre filtern"""
genre = request.query_params.get('genre')
if not genre:
return Response(
{'error': 'Genre parameter is required'},
status=status.HTTP_400_BAD_REQUEST
)
movies = Movie.objects.filter(genre__iexact=genre)
serializer = self.get_serializer(movies, many=True)
return Response(serializer.data)
@extend_schema_view(
list=extend_schema(summary="Liste aller Künstler", tags=['Artists']),
retrieve=extend_schema(summary="Künstler-Details", tags=['Artists']),
create=extend_schema(summary="Künstler erstellen", tags=['Artists']),
update=extend_schema(summary="Künstler aktualisieren", tags=['Artists']),
partial_update=extend_schema(summary="Künstler teilweise aktualisieren", tags=['Artists']),
destroy=extend_schema(summary="Künstler löschen", tags=['Artists']),
)
class ArtistViewSet(viewsets.ModelViewSet):
"""ViewSet für Artist CRUD Operationen"""
queryset = Artist.objects.all()
serializer_class = ArtistSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
@extend_schema_view(
list=extend_schema(summary="Liste aller Besetzungen", tags=['Castings']),
retrieve=extend_schema(summary="Besetzungs-Details", tags=['Castings']),
create=extend_schema(summary="Besetzung erstellen", tags=['Castings']),
update=extend_schema(summary="Besetzung aktualisieren", tags=['Castings']),
partial_update=extend_schema(summary="Besetzung teilweise aktualisieren", tags=['Castings']),
destroy=extend_schema(summary="Besetzung löschen", tags=['Castings']),
)
class MovieCastingViewSet(viewsets.ModelViewSet):
"""ViewSet für MovieCasting CRUD"""
queryset = MovieCasting.objects.select_related('movie', 'artist').all()
serializer_class = MovieCastingSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
# filepath: movies/views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from drf_spectacular.utils import extend_schema, OpenApiResponse
from .serializers import UserSerializer, UserDetailSerializer
@extend_schema(
summary="User registrieren",
description="Erstellt einen neuen User und gibt ein Authentifizierungs-Token zurück.",
tags=['Authentication'],
request=UserSerializer,
responses={
201: OpenApiResponse(
response=UserDetailSerializer,
description='User erfolgreich erstellt',
),
400: OpenApiResponse(
description='Validierungsfehler',
),
},
examples=[
OpenApiExample(
'Register Example',
value={
'username': 'john_doe',
'email': 'john@example.com',
'password': 'secret123',
'password2': 'secret123',
'first_name': 'John',
'last_name': 'Doe',
},
request_only=True,
),
],
)
@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
"""User registrieren & Token zurückgeben"""
# ... Implementation wie vorher
pass
@extend_schema(
summary="User login",
description="Authentifiziert einen User und gibt ein Token zurück.",
tags=['Authentication'],
request={
'type': 'object',
'properties': {
'username': {'type': 'string'},
'password': {'type': 'string'},
},
'required': ['username', 'password'],
},
responses={
200: OpenApiResponse(
description='Login erfolgreich',
),
401: OpenApiResponse(
description='Ungültige Credentials',
),
},
examples=[
OpenApiExample(
'Login Example',
value={
'username': 'john_doe',
'password': 'secret123',
},
request_only=True,
),
],
)
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
"""User login & Token zurückgeben"""
# ... Implementation wie vorher
pass
@extend_schema(
summary="User logout",
description="Löscht das Authentifizierungs-Token des aktuellen Users.",
tags=['Authentication'],
responses={
200: OpenApiResponse(description='Logout erfolgreich'),
401: OpenApiResponse(description='Nicht authentifiziert'),
},
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def logout(request):
"""User logout - Token löschen"""
# ... Implementation wie vorher
pass
@extend_schema(
summary="Aktueller User",
description="Gibt Informationen über den aktuell authentifizierten User zurück.",
tags=['Authentication'],
responses={
200: UserDetailSerializer,
401: OpenApiResponse(description='Nicht authentifiziert'),
},
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def user_profile(request):
"""Aktueller User"""
# ... Implementation wie vorher
pass
# Browser öffnen:
http://localhost:8000/api/schema/swagger-ui/
1. Endpoints anzeigen:
Authentication
POST /api/auth/register/ User registrieren
POST /api/auth/login/ User login
POST /api/auth/logout/ User logout
GET /api/auth/me/ Aktueller User
Movies
GET /api/movies/ Liste aller Filme
POST /api/movies/ Film erstellen
GET /api/movies/{id}/ Film-Details
PUT /api/movies/{id}/ Film aktualisieren
PATCH /api/movies/{id}/ Film teilweise aktualisieren
DELETE /api/movies/{id}/ Film löschen
GET /api/movies/top_rated/ Top-bewertete Filme
GET /api/movies/by_genre/ Filme nach Genre
Artists
GET /api/artists/ Liste aller Künstler
POST /api/artists/ Künstler erstellen
...
Castings
GET /api/castings/ Liste aller Besetzungen
POST /api/castings/ Besetzung erstellen
...
1. Register User:
POST /api/auth/register/
Body: {
"username": "john_doe",
"email": "john@example.com",
"password": "secret123",
"password2": "secret123"
}
Response: {
"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b",
"user": { ... }
}
2. Token in Swagger speichern:
- Oben rechts: "Authorize" Button klicken
- Eingabefeld: "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
- "Authorize" klicken
- ✅ Jetzt authenticated!
3. Protected Endpoints testen:
POST /api/movies/
- Funktioniert jetzt (mit Token)!
1. POST /api/movies/ aufklappen
2. "Try it out" Button klicken
3. Request Body editieren:
{
"title": "Interstellar",
"year": 2014,
"genre": "Sci-Fi",
"rating": 8.6,
"description": "A team of explorers travel through a wormhole."
}
4. "Execute" Button klicken
5. Response sehen:
- Status: 201 Created
- Response Body: { "id": 1, "title": "Interstellar", ... }
# settings.py
SPECTACULAR_SETTINGS = {
# ...
'TAGS': [
{
'name': 'Authentication',
'description': 'User Authentication & Authorization',
},
{
'name': 'Movies',
'description': 'Film-Verwaltung (CRUD)',
},
{
'name': 'Artists',
'description': 'Künstler-Verwaltung (CRUD)',
},
{
'name': 'Castings',
'description': 'Besetzungs-Verwaltung',
},
],
}
from drf_spectacular.utils import extend_schema
@extend_schema(
responses={
200: MovieSerializer(many=True),
400: {
'type': 'object',
'properties': {
'error': {'type': 'string'},
},
},
401: {
'type': 'object',
'properties': {
'detail': {'type': 'string'},
},
},
}
)
@action(detail=False, methods=['get'])
def custom_action(self, request):
pass
from drf_spectacular.utils import OpenApiParameter
from drf_spectacular.types import OpenApiTypes
@extend_schema(
parameters=[
OpenApiParameter(
name='search',
type=OpenApiTypes.STR,
location=OpenApiParameter.QUERY,
description='Suchbegriff',
required=False,
),
OpenApiParameter(
name='ordering',
type=OpenApiTypes.STR,
location=OpenApiParameter.QUERY,
description='Sortierung (z.B. "-rating")',
required=False,
),
]
)
def list(self, request):
pass
from drf_spectacular.utils import OpenApiExample
@extend_schema(
examples=[
OpenApiExample(
'Success Example',
value={'id': 1, 'title': 'Inception'},
response_only=True,
status_codes=['200'],
),
OpenApiExample(
'Error Example',
value={'error': 'Movie not found'},
response_only=True,
status_codes=['404'],
),
]
)
def retrieve(self, request, pk=None):
pass
# Browser:
http://localhost:8000/api/schema/ # JSON Format
http://localhost:8000/api/schema/?format=yaml # YAML Format
# Terminal (JSON):
curl http://localhost:8000/api/schema/ > schema.json
# Terminal (YAML):
curl http://localhost:8000/api/schema/?format=yaml > schema.yaml
# Django Management Command:
python manage.py spectacular --file schema.yaml --format openapi-yaml
# Installation:
npm install @openapitools/openapi-generator-cli -g
# JavaScript/TypeScript Client:
openapi-generator-cli generate \
-i http://localhost:8000/api/schema/ \
-g typescript-axios \
-o ./client
# Python Client:
openapi-generator-cli generate \
-i http://localhost:8000/api/schema/ \
-g python \
-o ./python-client
# Java Client:
openapi-generator-cli generate \
-i http://localhost:8000/api/schema/ \
-g java \
-o ./java-client
# Weitere: kotlin, swift, php, ruby, go, etc.
// Generierter Client:
import { MovieApi, Configuration } from './client';
// API Client konfigurieren
const config = new Configuration({
basePath: 'http://localhost:8000',
accessToken: 'your-token-here',
});
const movieApi = new MovieApi(config);
// API verwenden (type-safe!)
async function getMovies() {
const response = await movieApi.moviesListList();
console.log(response.data); // TypeScript weiß: Movie[]
}
async function createMovie() {
const movie = await movieApi.moviesCreateCreate({
title: 'Inception',
year: 2010,
genre: 'Sci-Fi',
rating: 8.8,
});
console.log(movie.data); // TypeScript weiß: Movie
}
class MovieViewSet(viewsets.ModelViewSet):
"""
ViewSet für Movie CRUD.
Bietet vollständige CRUD-Funktionalität
für Film-Verwaltung.
"""
def create(self, request, *args, **kwargs):
"""
Film erstellen.
Erstellt einen neuen Film in der Datenbank.
Authentifizierung erforderlich.
"""
pass
title = serializers.CharField(
max_length=200,
help_text="Titel des Films"
)
rating = serializers.DecimalField(
max_digits=3,
decimal_places=1,
help_text="Bewertung 0.0-10.0"
)
@extend_schema(
summary="Kurze Beschreibung",
description="Detaillierte Beschreibung",
tags=['Movies'],
parameters=[...],
responses={200: ...},
examples=[...],
)
@action(detail=False)
def custom_action(self, request):
pass
# Logische Gruppierung:
tags=['Authentication']
tags=['Movies']
tags=['Artists']
# In Swagger UI:
▼ Authentication
- POST /api/auth/login/
- POST /api/auth/register/
▼ Movies
- GET /api/movies/
- POST /api/movies/
examples=[
OpenApiExample(
'Success',
value={'id': 1, 'title': 'Inception'},
response_only=True,
),
OpenApiExample(
'Error',
value={'error': 'Not found'},
response_only=True,
),
]
# settings.py
SPECTACULAR_SETTINGS = {
'VERSION': '1.0.0', # Semantic Versioning
# Version in URL:
'SCHEMA_PATH_PREFIX': r'/api/v1/',
}
# URLs:
/api/v1/movies/
/api/v2/movies/ # Neue Version
# Swagger UI (interaktiv):
http://localhost:8000/api/schema/swagger-ui/
# ReDoc (schöne Doku):
http://localhost:8000/api/schema/redoc/
# Schema Download (JSON):
http://localhost:8000/api/schema/
# Schema Download (YAML):
http://localhost:8000/api/schema/?format=yaml
# 1. Installation:
pip install drf-spectacular
# 2. settings.py:
INSTALLED_APPS = ['drf_spectacular', ...]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = { ... }
# 3. urls.py:
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
urlpatterns = [
path('api/schema/', SpectacularAPIView.as_view()),
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view()),
path('api/schema/redoc/', SpectacularRedocView.as_view()),
]
# 4. Views dokumentieren:
@extend_schema(summary="...", description="...", tags=['Movies'])
# 5. Testen:
python manage.py runserver
http://localhost:8000/api/schema/swagger-ui/
Keep coding, keep documenting! 💻