Django Interview-Fragen: ORM, Middleware und DRF im Detail

Django Interview-Fragen zu ORM-Optimierung mit FETCH_PEERS, Middleware-Architektur und DRF-Serializer-Performance. Codebeispiele mit Django 6.1.

Django Interview-Vorbereitung zu ORM, Middleware und Django REST Framework

Django-Vorstellungsgespräche konzentrieren sich auf drei zentrale Bereiche, die erfahrene Entwickler von Einsteigern unterscheiden: ORM-Beherrschung, Middleware-Architektur und Django REST Framework (DRF) Designmuster. Dieser Leitfaden analysiert die konkreten Fragen, die technische Interviewer im Jahr 2026 stellen, mit produktionsreifen Codebeispielen für Django 6.1 und DRF 3.18.

Worauf Interviewer tatsächlich achten

Django-Interviews behandeln selten grundlegende CRUD-Operationen. Der Fokus liegt auf QuerySet-Optimierung (select_related vs prefetch_related, der neue FETCH_PEERS-Modus), benutzerdefiniertem Middleware-Design und DRF-Serializer-Performance. Kandidaten, die N+1-Queries erklären und effiziente ViewSets schreiben können, übertreffen konstant jene, die nur generische Views kennen.

Django ORM Interview-Fragen: QuerySet-Optimierung

Die häufigste Django ORM Interview-Frage zielt auf das N+1-Problem ab. Bei einem Modell mit Fremdschlüsselbeziehungen müssen Kandidaten demonstrieren, wann select_related gegenüber prefetch_related zu verwenden ist.

python
# models.py
from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=200)
    founded_year = models.IntegerField()

class Developer(models.Model):
    name = models.CharField(max_length=200)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="developers")
    skills = models.ManyToManyField("Skill", related_name="developers")

class Skill(models.Model):
    name = models.CharField(max_length=100)
    category = models.CharField(max_length=50)

Die Unterscheidung zwischen select_related und prefetch_related basiert auf der Art der Beziehung.

python
# queries.py — Correct approach for ForeignKey (single object)
developers = Developer.objects.select_related("company").all()
# Generates ONE SQL query with JOIN
# SELECT developer.*, company.* FROM developer INNER JOIN company ...

# Correct approach for ManyToMany (multiple objects)
developers = Developer.objects.prefetch_related("skills").all()
# Generates TWO SQL queries:
# 1. SELECT * FROM developer
# 2. SELECT * FROM skill INNER JOIN developer_skills WHERE developer_id IN (...)

select_related führt einen SQL-JOIN durch und funktioniert mit ForeignKey und OneToOneField. prefetch_related führt eine separate Abfrage aus und funktioniert mit ManyToManyField und reverse ForeignKey-Beziehungen. Die Verwechslung führt entweder zu unnötigen JOINs bei großen Datensätzen oder zum gefürchteten N+1-Muster.

Django 6.1: FETCH_PEERS-Modus zur automatischen N+1-Vermeidung

Django 6.1 führt ein neues Fetch-Modus-System ein, das N+1-Abfragen auf ORM-Ebene behandelt. Beim Iterieren über ein QuerySet und Zugriff auf ein verknüpftes Feld lädt FETCH_PEERS dieses Feld für alle Instanzen im QuerySet mit einer einzigen zusätzlichen Abfrage.

python
# views.py — Django 6.1 FETCH_PEERS mode
from django.db import models
from .models import Book

# Traditional approach: requires explicit prefetch_related
books = Book.objects.prefetch_related("author").all()

# Django 6.1 approach: automatic peer fetching
books = Book.objects.fetch_mode(models.FETCH_PEERS)
for book in books:
    print(book.author.name)  # Only 2 queries total: books + all authors

Drei Fetch-Modi stehen zur Verfügung:

  • FETCH_ONE (Standard): Lädt das fehlende Feld nur für die aktuelle Instanz
  • FETCH_PEERS: Lädt das Feld für alle Instanzen aus demselben QuerySet
  • FETCH_RAISE: Wirft FieldFetchBlocked für performancekritischen Code, bei dem Lazy Loading verhindert werden muss

Für Interview-Zwecke sollten Kandidaten beide Ansätze kennen: explizites select_related/prefetch_related für detaillierte Kontrolle und FETCH_PEERS für automatische Optimierung beim Iterieren über QuerySets.

Fortgeschrittenes ORM: Custom Managers und QuerySet-Verkettung

Interviewer fragen häufig nach der Implementierung eines Custom Managers, der Geschäftslogik kapselt. Ziel ist es zu überprüfen, ob der Kandidat Djangos Manager-Muster versteht und wiederverwendbare Query-Interfaces schreiben kann.

python
# managers.py
from django.db import models
from django.utils import timezone

class ActiveDeveloperQuerySet(models.QuerySet):
    def active(self):
        """Filter developers who logged in within the last 30 days."""
        cutoff = timezone.now() - timezone.timedelta(days=30)
        return self.filter(last_login__gte=cutoff)

    def senior(self):
        """Filter developers with 5+ years of experience."""
        return self.filter(years_experience__gte=5)

    def by_skill(self, skill_name):
        """Filter developers by a specific skill."""
        return self.filter(skills__name=skill_name)

class ActiveDeveloperManager(models.Manager):
    def get_queryset(self):
        return ActiveDeveloperQuerySet(self.model, using=self._db)

    def active(self):
        return self.get_queryset().active()

Die wichtige Folgefrage lautet: "Warum ein Custom QuerySet statt nur einen Manager verwenden?" Die Antwort ist Verkettung. Custom QuerySet-Methoden können miteinander verkettet werden, während Manager-Methoden nach dem ersten Aufruf nicht verkettet werden können.

python
# usage.py — QuerySet chaining in action
# This works because each method returns a QuerySet
senior_python_devs = (
    Developer.active_objects  # custom manager
    .active()                 # ActiveDeveloperQuerySet method
    .senior()                 # chains another QuerySet method
    .by_skill("Python")       # chains a third method
    .select_related("company")  # standard QuerySet method still works
)
Django 6.0: CompositePrimaryKey und Background Tasks

Django 6.0 führte CompositePrimaryKey für Modelle ein, die mehrspaltige Primärschlüssel benötigen, plus ein integriertes Background-Tasks-Framework. Interview-Fragen zu diesen Features werden häufiger, insbesondere für Kandidaten, die mit Legacy-Datenbanken oder asynchronen Verarbeitungsanforderungen arbeiten.

Django Middleware Interview-Fragen: Request-Response-Pipeline

Middleware-Fragen testen das Verständnis eines Kandidaten für Djangos Request-Response-Lebenszyklus. Die Standardfrage: "Erklären Sie die Reihenfolge, in der Middleware eine Anfrage und eine Antwort verarbeitet."

Die Antwort folgt einem strikten Muster. Während einer Anfrage werden Middleware-Klassen von oben nach unten ausgeführt, wie in MIDDLEWARE definiert. Während einer Antwort werden sie von unten nach oben ausgeführt. Diese Zwiebelschicht-Architektur bedeutet, dass die erste Middleware in der Liste alles umschließt.

python
# middleware.py
import time
import logging
from django.http import JsonResponse

logger = logging.getLogger(__name__)

class RequestTimingMiddleware:
    """Logs the time taken to process each request."""

    def __init__(self, get_response):
        self.get_response = get_response  # next middleware or view

    def __call__(self, request):
        start_time = time.monotonic()

        response = self.get_response(request)  # passes to next layer

        duration_ms = (time.monotonic() - start_time) * 1000
        logger.info(
            "method=%s path=%s status=%d duration=%.2fms",
            request.method,
            request.path,
            response.status_code,
            duration_ms,
        )
        return response

Eine häufige Folgefrage: "Wie würden Sie die Middleware-Kette kurzschließen?" Das Zurückgeben einer HttpResponse aus __call__ vor dem Aufruf von self.get_response(request) stoppt die Kette vollständig. Die restliche Middleware und die View werden nie ausgeführt.

python
# middleware.py — Rate limiting with short-circuit
from django.core.cache import cache

class RateLimitMiddleware:
    """Blocks requests exceeding 100 per minute per IP."""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        ip = request.META.get("REMOTE_ADDR")
        cache_key = f"rate_limit:{ip}"
        request_count = cache.get(cache_key, 0)

        if request_count >= 100:
            return JsonResponse(  # short-circuits — view never executes
                {"error": "Rate limit exceeded. Try again in 60 seconds."},
                status=429,
            )

        cache.set(cache_key, request_count + 1, timeout=60)
        return self.get_response(request)

Middleware-Hooks: process_view, process_exception und process_template_response

Neben __call__ unterstützt Django-Middleware drei optionale Hook-Methoden. Interviewer verwenden diese, um die Tiefe des Wissens zu bewerten.

python
# middleware.py — Full middleware with all hooks
class AuditMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_view(self, request, view_func, view_args, view_kwargs):
        """Called after URL resolution, before the view executes."""
        logger.info("Calling view: %s", view_func.__name__)
        return None  # returning None continues normal processing

    def process_exception(self, request, exception):
        """Called only if the view raises an exception."""
        logger.error("View exception: %s", exception, exc_info=True)
        return None  # returning None lets Django's default handling proceed

    def process_template_response(self, request, response):
        """Called if the response has a render() method (TemplateResponse)."""
        response.context_data["audit_timestamp"] = time.time()
        return response  # must return a response with render()

process_view wird nach der URL-Auflösung, aber vor der View ausgeführt. Die Rückgabe von None setzt die Ausführung fort; die Rückgabe einer HttpResponse führt zum Kurzschluss. process_exception wird nur bei unbehandelten Ausnahmen ausgelöst. process_template_response wird nur für TemplateResponse-Objekte ausgelöst, nicht für reguläre HttpResponse.

Bereit für deine Django-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

DRF Interview-Fragen: Serializer-Performance

Bei Fragen zu Django REST Framework-Serializern liegt der Fokus auf verschachtelter Serialisierung und den Performance-Auswirkungen verschiedener Ansätze. Die am häufigsten gestellte Frage: "Wie behandeln Sie verschachtelte Beziehungen, ohne N+1-Abfragen zu verursachen?"

python
# serializers.py
from rest_framework import serializers
from .models import Developer, Company, Skill

class SkillSerializer(serializers.ModelSerializer):
    class Meta:
        model = Skill
        fields = ["name", "category"]

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ["name", "founded_year"]

class DeveloperSerializer(serializers.ModelSerializer):
    company = CompanySerializer(read_only=True)   # nested FK
    skills = SkillSerializer(many=True, read_only=True)  # nested M2M

    class Meta:
        model = Developer
        fields = ["id", "name", "company", "skills"]

Der Serializer allein löst das Performance-Problem nicht. Das ViewSet muss das QuerySet optimieren.

python
# views.py
from rest_framework import viewsets
from .models import Developer
from .serializers import DeveloperSerializer

class DeveloperViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = DeveloperSerializer

    def get_queryset(self):
        return (
            Developer.objects
            .select_related("company")       # JOIN for FK
            .prefetch_related("skills")      # separate query for M2M
            .order_by("-id")
        )

Ohne select_related und prefetch_related im ViewSet löst jeder serialisierte Entwickler individuelle Abfragen für seine Firma und Fähigkeiten aus. Bei einer Liste von 50 Entwicklern bedeutet das 1 + 50 + 50 = 101 Abfragen statt 3.

DRF Benutzerdefinierte Berechtigungen und Authentifizierung

Eine häufige DRF-Interview-Frage: "Schreiben Sie eine benutzerdefinierte Berechtigung, die den Zugriff basierend auf der Rolle des Benutzers und dem Eigentümer des Objekts einschränkt."

python
# permissions.py
from rest_framework.permissions import BasePermission

class IsOwnerOrAdmin(BasePermission):
    """
    Object-level permission:
    - Admin users can access any object
    - Regular users can only access objects they own
    """
    message = "Access restricted to the object owner or admin users."

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True
        # Assumes the model has an 'owner' field pointing to User
        return obj.owner == request.user
python
# views.py — Applying custom permissions
from rest_framework import viewsets, permissions
from .permissions import IsOwnerOrAdmin

class ProjectViewSet(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdmin]

    def get_queryset(self):
        # Non-admin users only see their own projects
        if self.request.user.is_staff:
            return Project.objects.all()
        return Project.objects.filter(owner=self.request.user)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)  # auto-assign owner

Die Nuance, nach der Interviewer suchen: has_permission wird bei jeder Anfrage ausgeführt (Listen-Ebene), während has_object_permission nur ausgeführt wird, wenn get_object() aufgerufen wird (Detail-Ebene). Das Vergessen, get_queryset für List-Views zu überschreiben, erzeugt eine Sicherheitslücke, bei der Benutzer Objekte sehen können, die ihnen nicht gehören.

Häufiger DRF-Sicherheitsfehler

Das alleinige Verlassen auf has_object_permission ohne QuerySet-Filterung lässt den List-Endpoint ungeschützt. Object-Level-Permissions sollten stets mit einem gefilterten get_queryset kombiniert werden, um Zugriffskontrolle sowohl auf List- als auch Detail-Views durchzusetzen.

DRF Throttling und Pagination-Muster

Throttling und Pagination sind Standard-Folgefragen. Interviewer möchten sehen, dass Kandidaten produktionsreife API-Konfiguration verstehen.

python
# settings.py — Production DRF configuration
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
        "rest_framework.throttling.UserRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {
        "anon": "20/minute",    # unauthenticated users
        "user": "200/minute",   # authenticated users
    },
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination",
    "PAGE_SIZE": 25,
}
python
# pagination.py — Custom cursor pagination for consistent ordering
from rest_framework.pagination import CursorPagination

class CreatedAtCursorPagination(CursorPagination):
    page_size = 25
    ordering = "-created_at"       # must be a unique, sequential field
    cursor_query_param = "cursor"  # ?cursor=abc123

Cursor-basierte Pagination übertrifft Offset-Pagination bei großen Datensätzen, da sie keine Gesamtzeilenzählung erfordert. Der Kompromiss: Clients können nicht zu beliebigen Seiten springen. Dies ist die erwartete Antwort, wenn Interviewer fragen: "Warum würden Sie Cursor-Pagination gegenüber Seitennummer-Pagination wählen?"

Quellen

Wichtige Erkenntnisse für die Django-Interview-Vorbereitung

  • ORM-Optimierung: Die Query-Methode muss zum Beziehungstyp passen. select_related für ForeignKey/OneToOne, prefetch_related für ManyToMany und reverse FK. Der FETCH_PEERS-Modus von Django 6.1 bietet automatische N+1-Vermeidung beim Iterieren über QuerySets.
  • Custom QuerySets: Ermöglichen verkettbare, wiederverwendbare Geschäftslogik. Custom Manager-Methoden können nach dem ersten Aufruf nicht verkettet werden, aber QuerySet-Methoden können es.
  • Middleware-Architektur: Die Anfrage fließt von oben nach unten, die Antwort von unten nach oben. Das Kurzschließen durch Rückgabe einer Antwort vor get_response() ist ein grundlegendes Muster für Rate Limiting, Auth-Checks und Request-Validierung.
  • DRF-Serializer-Performance: Verschachtelte Serializer erfordern explizite QuerySet-Optimierung im ViewSet. Ohne select_related/prefetch_related verursacht die Serialisierung N+1-Abfragen bei Skalierung.
  • DRF-Berechtigungen: Kombinieren Sie has_object_permission mit einem gefilterten get_queryset, um Zugriffskontrolle sowohl auf List- als auch Detail-Endpoints durchzusetzen. Object-Level-Permissions allein lassen List-Views ungeschützt.
  • Throttling und Pagination: Cursor-Pagination skaliert besser als Offset bei großen Tabellen. Konfigurieren Sie Throttle-Raten separat für anonyme und authentifizierte Benutzer.

Üben Sie diese Muster mit Django ORM Interview-Fragen und Django Middleware-Fragen auf SharpSkill, um vor dem echten Interview Sicherheit aufzubauen.

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Tägliche Challenge

Findest du den Bug in Django?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 13. September 2026

Tags

#django
#python
#interview
#orm
#middleware
#drf
#rest-api

Teilen

Verwandte Artikel