Django REST Framework: Best Practices für skalierbare APIs
Inhalt
DRF ist seit Jahren mein tägliches Werkzeug. In dieser Zeit habe ich APIs von einfachen CRUD-Endpunkten bis zu Systemen mit tausenden gleichzeitigen Requests gebaut. Hier sind die Patterns die mir am meisten gebracht haben.
Serializer richtig einsetzen
Ein häufiger Fehler: einen einzigen Serializer für alle Operationen verwenden. Besser: separate Serializer je nach Zweck:
class ArticleListSerializer(serializers.ModelSerializer):
"""Kompakt: nur fuer die Listenansicht"""
author_name = serializers.CharField(source="author.get_full_name", read_only=True)
class Meta:
model = Article
fields = ["id", "title", "author_name", "created_at"]
class ArticleDetailSerializer(serializers.ModelSerializer):
"""Vollstaendig: fuer Detailansicht"""
author = AuthorSerializer(read_only=True)
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = "__all__"ViewSets vs. APIViews
ViewSets reduzieren Boilerplate erheblich. Für Standard-CRUD reicht ein ModelViewSet:
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.select_related("author").all()
permission_classes = [IsAuthenticatedOrReadOnly]
def get_serializer_class(self):
if self.action == "list":
return ArticleListSerializer
return ArticleDetailSerializer
@action(detail=True, methods=["post"])
def publish(self, request, pk=None):
article = self.get_object()
article.status = "published"
article.save()
return Response({"status": "published"})Authentication und Permissions
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
# Custom Permission
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.author == request.userPagination und Filtering
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework.filters.SearchFilter",
"rest_framework.filters.OrderingFilter",
],
}
# Im ViewSet:
filterset_fields = ["status", "category"]
search_fields = ["title", "body"]
ordering_fields = ["created_at", "title"]
ordering = ["-created_at"]Throttling
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/hour",
"user": "1000/hour",
}
}API-Versionierung
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1", "v2"],
}
# Im ViewSet auf Version reagieren:
def get_serializer_class(self):
if self.request.version == "v2":
return ArticleV2Serializer
return ArticleSerializer