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.

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 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.

Klaar om je Django gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

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.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Tags

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

Delen

Gerelateerde artikelen