Django Sollicitatievragen: ORM, Middleware en DRF Diepgaand Behandeld

Django sollicitatievragen over ORM-optimalisatie met FETCH_PEERS, middleware-architectuur en DRF serializer-prestaties. Codevoorbeelden met Django 6.1.

Django sollicitatievoorbereiding over ORM, Middleware en Django REST Framework

Django sollicitatievragen richten zich op drie fundamentele pijlers die senior kandidaten onderscheiden van de rest: ORM-beheersing, middleware-architectuur en Django REST Framework (DRF) ontwerppatronen. Deze gids behandelt de exacte vragen die technisch recruiters stellen in 2026, inclusief productie-ready codevoorbeelden met Django 6.1 en DRF 3.18.

Waar interviewers daadwerkelijk op testen

Django-interviews vragen zelden naar basis CRUD-operaties. De focus is verschoven naar QuerySet-optimalisatie (select_related versus prefetch_related, de nieuwe FETCH_PEERS-modus), aangepast middleware-ontwerp en DRF serializer-prestaties. Kandidaten die N+1 queries kunnen uitleggen en efficiënte viewsets kunnen schrijven, presteren consequent beter dan degenen die alleen generieke views kennen.

Django ORM Sollicitatievragen: QuerySet Optimalisatie

De meest voorkomende Django ORM sollicitatievraag richt zich op het N+1 probleem. Bij een model met foreign key relaties moeten kandidaten demonstreren wanneer select_related versus prefetch_related gebruikt moet worden.

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)

Het verschil tussen de twee methoden hangt af van het type relatie dat wordt doorlopen.

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 voert een SQL JOIN uit en werkt met ForeignKey en OneToOneField. prefetch_related voert een aparte query uit en werkt met ManyToManyField en omgekeerde ForeignKey relaties. Ze door elkaar halen veroorzaakt ofwel onnodige JOINs op grote datasets of het gevreesde N+1 patroon.

Django 6.1: FETCH_PEERS Modus voor Automatische N+1 Preventie

Django 6.1 introduceert een nieuw fetch mode systeem dat N+1 queries op ORM-niveau aanpakt. Bij het itereren over een queryset en toegang tot een gerelateerd veld, haalt FETCH_PEERS dat veld op voor alle instanties in de queryset met een enkele extra query.

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

Drie fetch modes zijn beschikbaar:

  • FETCH_ONE (standaard): Haalt het ontbrekende veld alleen voor de huidige instantie op
  • FETCH_PEERS: Haalt het veld op voor alle instanties uit dezelfde QuerySet
  • FETCH_RAISE: Gooit FieldFetchBlocked voor prestatie-kritieke code waar lazy loading moet worden voorkomen

Voor sollicitatiedoeleinden moeten kandidaten beide benaderingen kennen: expliciet select_related/prefetch_related voor fijnmazige controle, en FETCH_PEERS voor automatische optimalisatie bij het itereren over querysets.

Geavanceerde ORM: Custom Managers en QuerySet Chaining

Interviewers vragen kandidaten vaak om een custom manager te schrijven die bedrijfslogica inkapselt. Het doel is te verifiëren dat de kandidaat Djangos manager-patroon begrijpt en herbruikbare query-interfaces kan schrijven.

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()

De belangrijke vervolgvraag: "Waarom een custom QuerySet gebruiken in plaats van alleen een Manager?" Het antwoord is chaining. Custom QuerySet-methoden kunnen aan elkaar worden gekoppeld, terwijl Manager-methoden niet kunnen worden gekoppeld na de eerste aanroep.

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 en Background Tasks

Django 6.0 introduceerde CompositePrimaryKey voor modellen die meerdere kolommen als primaire sleutel nodig hebben, plus een ingebouwd background tasks framework. Sollicitatievragen over deze features worden steeds gebruikelijker, vooral voor kandidaten die werken met legacy databases of asynchrone verwerkingseisen.

Django Middleware Sollicitatievragen: Request-Response Pipeline

Middleware-vragen testen het begrip van een kandidaat van Djangos request-response levenscyclus. De standaardvraag: "Leg de volgorde uit waarin middleware een request en een response verwerkt."

Het antwoord volgt een strikt patroon. Tijdens een request worden middleware-klassen van boven naar beneden uitgevoerd zoals gedefinieerd in MIDDLEWARE. Tijdens een response worden ze van beneden naar boven uitgevoerd. Deze ui-laag architectuur betekent dat de eerste middleware in de lijst alles omhult.

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

Een veelgestelde vervolgvraag: "Hoe zou je de middleware-keten kortsluiten?" Het retourneren van een HttpResponse vanuit __call__ vóór het aanroepen van self.get_response(request) stopt de keten volledig. De resterende middleware en de view worden nooit uitgevoerd.

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 en process_template_response

Naast __call__ ondersteunt Django middleware drie optionele hook-methoden. Interviewers gebruiken deze om de diepte van kennis te peilen.

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 wordt uitgevoerd na URL-resolutie maar vóór de view. Het retourneren van None zet de uitvoering voort; het retourneren van een HttpResponse kortsluit. process_exception wordt alleen uitgevoerd bij onafgehandelde exceptions. process_template_response wordt alleen uitgevoerd voor TemplateResponse objecten, niet voor reguliere HttpResponse.

Klaar om je Django gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

DRF Sollicitatievragen: Serializer Prestaties

Django REST Framework serializer-vragen richten zich op geneste serialisatie en de prestatie-implicaties van verschillende benaderingen. De meest gestelde vraag: "Hoe ga je om met geneste relaties zonder N+1 queries te veroorzaken?"

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"]

De serializer alleen lost de prestaties niet op. De ViewSet moet de queryset optimaliseren.

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")
        )

Zonder select_related en prefetch_related in de ViewSet triggert elke geserialiseerde developer individuele queries voor zijn bedrijf en vaardigheden. Bij een lijst van 50 developers betekent dat 1 + 50 + 50 = 101 queries in plaats van 3.

DRF Custom Permissions en Authenticatie

Een veelgestelde DRF sollicitatievraag: "Schrijf een custom permission die toegang beperkt op basis van de gebruikersrol en de eigenaar van het object."

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

De nuance waar interviewers naar zoeken: has_permission wordt uitgevoerd bij elk verzoek (lijst-niveau), terwijl has_object_permission alleen wordt uitgevoerd wanneer get_object() wordt aangeroepen (detail-niveau). Vergeten om get_queryset te overschrijven voor list views creëert een beveiligingslek waar gebruikers objecten kunnen zien die niet van hen zijn.

Veelgemaakte DRF-beveiligingsfout

Het uitsluitend vertrouwen op has_object_permission zonder de queryset te filteren laat de lijst-endpoint onbeschermd. Combineer altijd object-level permissions met een gefilterde get_queryset om toegangscontrole af te dwingen op zowel lijst- als detail-views.

DRF Throttling en Pagination Patronen

Throttling en pagination zijn standaard vervolgvragen. Interviewers willen zien dat kandidaten productie-ready API-configuratie begrijpen.

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-gebaseerde pagination presteert beter dan offset pagination op grote datasets omdat het geen totale rijtelling vereist. De afweging: clients kunnen niet naar willekeurige pagina's springen. Dit is het verwachte antwoord wanneer interviewers vragen "Waarom zou je cursor pagination kiezen boven paginanummer pagination?"

Bronnen

Kernpunten voor Django Sollicitatievoorbereiding

  • ORM-optimalisatie: Match de query-methode met het relatietype. select_related voor ForeignKey/OneToOne, prefetch_related voor ManyToMany en omgekeerde FK. De FETCH_PEERS modus van Django 6.1 biedt automatische N+1 preventie bij het itereren over querysets.
  • Custom QuerySets: Maken chainbare, herbruikbare bedrijfslogica mogelijk. Custom Manager-methoden kunnen niet worden gekoppeld na de eerste aanroep, maar QuerySet-methoden wel.
  • Middleware-architectuur: Het request stroomt van boven naar beneden, de response van beneden naar boven. Kortsluiten door een response te retourneren vóór get_response() is een fundamenteel patroon voor rate limiting, auth checks en request validatie.
  • DRF serializer-prestaties: Geneste serializers vereisen expliciete queryset-optimalisatie in de ViewSet. Zonder select_related/prefetch_related veroorzaakt serialisatie N+1 queries op schaal.
  • DRF permissions: Combineer has_object_permission met een gefilterde get_queryset om toegangscontrole af te dwingen op zowel list als detail endpoints. Object-level permissions alleen laten list views onbeschermd.
  • Throttling en pagination: Cursor pagination schaalt beter dan offset op grote tabellen. Configureer throttle rates apart voor anonieme en geauthenticeerde gebruikers.

Oefen deze patronen met Django ORM sollicitatievragen en Django middleware vragen op SharpSkill om vertrouwen op te bouwen vóór het echte sollicitatiegesprek.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Dagelijkse challenge

Zie jij de bug in Django?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 13 september 2026

Tags

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

Delen

Gerelateerde artikelen