# Django Sollicitatievragen: ORM, Middleware en DRF Diepgaand Behandeld > Uitgebreide gids voor Django sollicitatievragen over ORM-optimalisatie, middleware-architectuur en Django REST Framework. Met productie-ready codevoorbeelden voor Django 5.2. - Published: 2026-04-03 - Updated: 2026-04-03 - Author: SharpSkill - Tags: django, python, interview, orm, middleware, drf, rest-api - Reading time: 9 min --- 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 5.2 LTS en DRF 3.17. Het beheersen van deze drie domeinen bepaalt vaak het verschil tussen een geslaagd en een mislukt technisch interview. Bedrijven zoeken ontwikkelaars die niet alleen de basis kennen, maar ook complexe optimalisatieproblemen kunnen oplossen en schaalbare architecturen kunnen ontwerpen. De vragen in deze gids weerspiegelen de werkelijke uitdagingen die Django-ontwikkelaars tegenkomen in productieomgevingen. > **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), 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. Dit onderscheid is cruciaal voor de prestaties van elke Django-applicatie. Het N+1 probleem ontstaat wanneer een applicatie eerst een query uitvoert om een lijst objecten op te halen, en vervolgens voor elk object een aparte query uitvoert om gerelateerde data op te halen. Bij honderd objecten resulteert dit in 101 queries in plaats van twee of drie geoptimaliseerde queries. ```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) ``` De keuze tussen `select_related` en `prefetch_related` hangt af van het type relatie. Voor ForeignKey en OneToOne relaties is `select_related` de juiste keuze omdat het een SQL JOIN uitvoert. Voor ManyToMany en reverse ForeignKey relaties is `prefetch_related` noodzakelijk omdat deze relaties meerdere objecten per instantie kunnen bevatten. ```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 (...) ``` Interviewers verwachten dat kandidaten kunnen uitleggen waarom deze scheiding bestaat. `select_related` werkt via een database JOIN, wat efficiënt is voor enkelvoudige relaties maar problematisch wordt bij meervoudige relaties vanwege de cartesische productvorming. `prefetch_related` voert aparte queries uit en combineert de resultaten in Python, wat efficiënter is voor meervoudige relaties. ## Geavanceerde ORM: Custom Managers en QuerySet Chaining Een veelgestelde vraag tijdens Django-interviews betreft het implementeren van herbruikbare query-logica via custom managers en QuerySets. Dit patroon maakt code leesbaarder en voorkomt duplicatie van filterlogica door de applicatie heen. Custom QuerySets stellen ontwikkelaars in staat om methoden te definiëren die geketend kunnen worden, vergelijkbaar met de ingebouwde Django QuerySet-methoden. Dit patroon volgt het fluent interface ontwerppatroon en maakt complexe queries zowel leesbaar als onderhoudbaar. ```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() ``` Het koppelen van een custom manager aan een model gebeurt via een class-attribuut. Het is belangrijk om zowel de standaard `objects` manager te behouden als de custom manager toe te voegen, zodat alle standaard Django-functionaliteit beschikbaar blijft. ```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 ) ``` Dit patroon demonstreert een kernprincipe van goed Django-ontwerp: het scheiden van query-logica van views en het centraliseren van bedrijfsregels in herbruikbare componenten. Interviewers waarderen kandidaten die dit patroon beheersen omdat het wijst op ervaring met grotere, complexere codebases. > **Django 5.2 LTS: Samengestelde Primaire Sleutels** > > Django 5.2 introduceerde CompositePrimaryKey, een langverwachte functie voor modellen die meerdere kolommen als primaire sleutel nodig hebben. Sollicitatievragen over deze functie worden steeds gebruikelijker, vooral voor kandidaten die werken met legacy databases of data warehouse schema's. ## Django Middleware Sollicitatievragen: Request-Response Pipeline Middleware vormt de ruggengraat van Django's request-response verwerking. Elke HTTP-request passeert door een geordende reeks middleware-klassen voordat deze de view bereikt, en elke response passeert in omgekeerde volgorde terug. Dit mechanisme maakt cross-cutting concerns zoals authenticatie, logging en rate limiting mogelijk zonder views te vervuilen met herhalende code. De basisstructuur van middleware in Django volgt het decorator-patroon. De `__init__` methode ontvangt een referentie naar de volgende laag in de stack, en de `__call__` methode verwerkt elke request. Dit ontwerp maakt middleware zowel flexibel als performant. ```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 cruciale vaardigheid die interviewers testen is het vermogen om requests te kortsluiten. Dit betekent dat middleware een response retourneert zonder de view aan te roepen, wat essentieel is voor beveiligingsmechanismen zoals rate limiting en toegangscontrole. ```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) ``` Deze implementatie demonstreert het kortsluitmechanisme: wanneer de rate limit overschreden wordt, retourneert de middleware direct een JsonResponse zonder `self.get_response()` aan te roepen. De view en alle onderliggende middleware worden volledig overgeslagen. ## Middleware Hooks: process_view, process_exception en process_template_response Naast de basis `__call__` methode ondersteunt Django middleware drie optionele hooks die op specifieke momenten in de request-response cyclus worden aangeroepen. Het begrijpen van deze hooks is essentieel voor het implementeren van geavanceerde functionaliteit. De `process_view` hook wordt aangeroepen na URL-resolutie maar voordat de view wordt uitgevoerd. Dit maakt het mogelijk om view-specifieke logica te implementeren, zoals het loggen van welke view wordt aangeroepen of het uitvoeren van view-specifieke autorisatiecontroles. De `process_exception` hook wordt alleen aangeroepen wanneer een view een niet-afgevangen exception gooit. Dit is de ideale locatie voor centrale foutafhandeling en logging. De `process_template_response` hook wordt aangeroepen wanneer de response een `render()` methode heeft, typisch bij `TemplateResponse` objecten. Dit maakt het mogelijk om template-context aan te passen voordat de template daadwerkelijk wordt gerenderd. ```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() ``` Het retourneren van `None` vanuit deze hooks geeft aan dat Django de normale verwerking moet voortzetten. Het retourneren van een `HttpResponse` object vanuit `process_view` of `process_exception` kortsluit de verdere verwerking, vergelijkbaar met het kortsluiten in de `__call__` methode. ## DRF Sollicitatievragen: Serializer Prestaties Django REST Framework serializers zijn verantwoordelijk voor het converteren van complexe data-types naar JSON en vice versa. Bij geneste relaties kunnen serializers echter ernstige prestatieproblemen veroorzaken als ze niet correct worden geconfigureerd met de juiste QuerySet-optimalisaties. Het definiëren van geneste serializers is straightforward, maar de prestatie-implicaties worden vaak over het hoofd gezien. Elke geneste serializer die een relatie volgt zonder corresponderende `select_related` of `prefetch_related` in de QuerySet veroorzaakt additionele database queries. ```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 corresponderende ViewSet moet de QuerySet correct optimaliseren. Dit is waar ORM-kennis en DRF-kennis samenkomen in praktische interviewvragen. Interviewers verwachten dat kandidaten automatisch de juiste optimalisaties toepassen bij het schrijven van ViewSets. ```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") ) ``` Deze combinatie van serializer-definitie en QuerySet-optimalisatie resulteert in exact drie database queries ongeacht het aantal developers: een voor de developers met company JOIN, een voor alle relevante skills, en de automatische koppeling die Django in Python uitvoert. ## DRF Custom Permissions en Authenticatie Object-level permissions zijn een geavanceerd DRF-concept dat regelmatig in interviews wordt getest. In tegenstelling tot standaard permissions die op view-niveau werken, controleren object-level permissions toegang tot individuele objecten. Het implementeren van custom permissions vereist het subclassen van `BasePermission` en het overschrijven van `has_permission` voor view-level checks of `has_object_permission` voor object-level checks. De combinatie van beide methoden biedt fijnmazige toegangscontrole. ```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 ``` Het toepassen van custom permissions op ViewSets vereist aandacht voor zowel de `permission_classes` als de `get_queryset` methode. Dit is een veelgemaakte fout die interviewers specifiek testen: het vertrouwen op alleen object-level permissions zonder de queryset te filteren laat de lijst-endpoint onbeschermd. ```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 ``` Deze implementatie demonstreert defense in depth: de gefilterde queryset voorkomt dat gebruikers objecten in de lijst zien die niet van hen zijn, terwijl de object-level permission een tweede verdedigingslinie biedt voor directe toegang via object-ID. > **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 essentiële componenten voor productie-ready API's. DRF biedt ingebouwde ondersteuning voor beide, maar interviewers verwachten dat kandidaten de configuratie kunnen aanpassen aan specifieke vereisten. De standaard DRF-configuratie ondersteunt verschillende throttling-klassen voor geanonimiseerde en geauthenticeerde gebruikers. Dit onderscheid is belangrijk voor API's die zowel publieke als private endpoints aanbieden. ```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", "user": "200/minute", }, "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination", "PAGE_SIZE": 25, } ``` Cursor-based pagination verdient speciale aandacht in interviews omdat het significante voordelen biedt boven offset-based pagination voor grote datasets. Cursor pagination voorkomt de prestatieproblemen van hoge offset-waarden en garandeert consistente resultaten zelfs wanneer nieuwe records worden toegevoegd tijdens het pagineren. ```python # pagination.py — Custom cursor pagination for consistent ordering from rest_framework.pagination import CursorPagination class CreatedAtCursorPagination(CursorPagination): page_size = 25 ordering = "-created_at" cursor_query_param = "cursor" ``` Het veld dat voor ordering wordt gebruikt moet uniek en sequentieel zijn om correcte cursor-pagination te garanderen. Typische keuzes zijn auto-increment primaire sleutels of timestamps met voldoende precisie. Het combineren van meerdere velden voor ordering is mogelijk maar vereist zorgvuldige overweging van de resulterende query-prestaties. ## Conclusie: Kernpunten voor Django Sollicitaties Succesvolle Django-sollicitatiegesprekken vereisen diepgaande kennis van de drie kernpijlers die in deze gids zijn behandeld. Kandidaten die deze concepten beheersen, onderscheiden zich consistent van minder ervaren sollicitanten. De belangrijkste punten om te onthouden: - **ORM-optimalisatie**: Beheers het verschil tussen `select_related` (ForeignKey, OneToOne) en `prefetch_related` (ManyToMany, reverse FK) om N+1 queries te voorkomen - **Custom Managers**: Implementeer herbruikbare query-logica via custom QuerySets en managers voor schonere, onderhoudbare code - **Middleware-architectuur**: Begrijp de request-response pipeline, kortsluitmechanismen en de drie middleware-hooks voor geavanceerde functionaliteit - **DRF Serializer-prestaties**: Combineer geneste serializers altijd met corresponderende QuerySet-optimalisaties in ViewSets - **Object-level Permissions**: Implementeer defense in depth door object-level permissions te combineren met gefilterde querysets - **Cursor Pagination**: Gebruik cursor-based pagination voor grote datasets om consistente prestaties en resultaten te garanderen Deze kennis vormt de basis voor het bouwen van schaalbare, performante Django-applicaties en demonstreert de technische diepgang die werkgevers zoeken in senior Django-ontwikkelaars. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/django/django-interview-orm-middleware-drf