Django en Python sollicitatievragen: De Top 25 in 2026
De 25 meest voorkomende Django- en Python-sollicitatievragen. ORM, views, middleware, DRF, signals en optimalisatie met gedetailleerde antwoorden en codevoorbeelden.

Django-sollicitatiegesprekken beoordelen de vaardigheid met Pythons populairste webframework, het begrip van de ORM, de MVT-architectuur en het vermogen om robuuste REST-API's te bouwen. Deze gids behandelt de 25 meest gestelde vragen, van Django-basisprincipes tot geavanceerde Django REST Framework-patronen.
Interviewers waarderen kandidaten die de architecturale keuzes van Django kunnen toelichten. Begrijpen waarom het framework bepaalde conventies hanteert (convention over configuration) maakt een echt verschil tijdens sollicitatiegesprekken.
Django-basisprincipes
Vraag 1: Beschrijf de MVT-architectuur van Django
De Model-View-Template (MVT)-architectuur is Djangos variant van het MVC-patroon. Het framework verzorgt het controller-gedeelte automatisch, wat de ontwikkeling vereenvoudigt.
# models.py
# The Model represents data structure and business logic
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
"auth.User",
on_delete=models.CASCADE, # Deletes articles when author is deleted
related_name="articles" # Reverse access: user.articles.all()
)
class Meta:
ordering = ["-published_at"] # Default ordering
def __str__(self):
return self.title# views.py
# The View contains request processing logic
from django.shortcuts import render, get_object_or_404
def article_detail(request, pk):
# get_object_or_404 raises Http404 if the object doesn't exist
article = get_object_or_404(Article, pk=pk)
return render(request, "blog/article_detail.html", {"article": article})In MVT fungeert Django als controller door URL's via urls.py naar de juiste views te routeren. Het Template verzorgt de HTML-presentatie.
Vraag 2: Wat is het verschil tussen een Django-project en een app?
Een project is de overkoepelende configuratie (instellingen, root-URL's, WSGI/ASGI). Een app is een herbruikbare module met een enkele verantwoordelijkheid. Een project bevat meerdere apps.
# Creating a project and an app
# django-admin startproject myproject
# python manage.py startapp blog
# settings.py
# Registering apps in the project
INSTALLED_APPS = [
"django.contrib.admin", # Admin interface
"django.contrib.auth", # Authentication
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Custom apps
"blog.apps.BlogConfig", # Blog app
"users.apps.UsersConfig", # Users app
]Elke app volgt het single-responsibility-principe en kan in verschillende projecten worden hergebruikt.
Vraag 3: Leg de Django request lifecycle uit
De aanvraag doorloopt meerdere lagen voordat deze de view bereikt. Het begrijpen van deze cyclus is essentieel voor debugging en optimalisatie.
# middleware.py
# Middlewares intercept every request/response
class RequestTimingMiddleware:
"""Middleware that measures processing time."""
def __init__(self, get_response):
self.get_response = get_response # Reference to the next middleware
def __call__(self, request):
import time
start = time.time()
# Request phase: before the view
response = self.get_response(request)
# Response phase: after the view
duration = time.time() - start
response["X-Request-Duration"] = f"{duration:.3f}s"
return responseDe volledige cyclus: HTTP-aanvraag → WSGI/ASGI → middlewares (process_request) → URL-resolver → view → middlewares (process_response) → HTTP-antwoord.
ORM en database
Vraag 4: Hoe werken Django's QuerySets en wat is lazy loading?
QuerySets worden lazy geevalueerd: er wordt geen SQL-query uitgevoerd totdat de data daadwerkelijk wordt gebruikt.
# queryset_lazy.py
# Demonstrating QuerySet lazy loading
# No SQL query executed here
qs = Article.objects.filter(published=True) # No query
qs = qs.exclude(title="Draft") # Still none
qs = qs.order_by("-created_at") # Still none
# The SQL query runs ONLY here
for article in qs: # ONE combined SQL query
print(article.title)
# Other evaluation triggers
list(qs) # Converting to list
qs[0] # Index access
len(qs) # Counting (prefer qs.count())
bool(qs) # Existence check (prefer qs.exists())Deze uitgestelde evaluatie maakt het mogelijk om filters te ketenen zonder overhead, waarbij slechts een geoptimaliseerde query wordt uitgevoerd.
Vraag 5: Wat is het N+1-probleem en hoe los je het op?
Het N+1-probleem ontstaat wanneer een hoofdquery N aanvullende queries genereert om relaties te laden. Het is de meest voorkomende oorzaak van traagheid in Django-applicaties.
# n_plus_one.py
# N+1 problem and solutions
# ❌ PROBLEM: N+1 queries
# 1 query for articles + 1 query PER article for the author
articles = Article.objects.all()
for article in articles:
print(article.author.username) # SQL query on every iteration!
# ✅ SOLUTION 1: select_related (ForeignKey, OneToOne)
# Joins tables in ONE SQL query (JOIN)
articles = Article.objects.select_related("author").all()
for article in articles:
print(article.author.username) # No additional query
# ✅ SOLUTION 2: prefetch_related (ManyToMany, reverse FK)
# Executes 2 separate queries + Python assembly
articles = Article.objects.prefetch_related("tags").all()
for article in articles:
print(article.tags.all()) # Data already cached
# ✅ SOLUTION 3: Custom Prefetch with filtering
from django.db.models import Prefetch
articles = Article.objects.prefetch_related(
Prefetch(
"comments",
queryset=Comment.objects.filter(approved=True).select_related("user"),
to_attr="approved_comments" # Custom attribute
)
)Gebruik select_related voor ForeignKey/OneToOne-relaties (SQL JOIN) en prefetch_related voor ManyToMany- of omgekeerde relaties (afzonderlijke queries).
Vraag 6: Hoe maak je een Custom Manager en wanneer zet je deze in?
Custom Managers kapselen veelgebruikte queries in op modelniveau, waardoor de code leesbaarder en herbruikbaarder wordt.
# managers.py
# Custom Managers and QuerySets
class PublishedQuerySet(models.QuerySet):
"""Reusable QuerySet for published articles."""
def published(self):
return self.filter(status="published", published_at__lte=timezone.now())
def by_author(self, user):
return self.filter(author=user)
def popular(self):
return self.annotate(
comment_count=models.Count("comments")
).order_by("-comment_count")
class PublishedManager(models.Manager):
"""Manager that exposes only published articles."""
def get_queryset(self):
return PublishedQuerySet(self.model, using=self._db).published()
class Article(models.Model):
# ...
objects = models.Manager() # Default manager (all articles)
published = PublishedManager() # Custom manager (published only)
# Usage:
# Article.objects.all() → All articles
# Article.published.all() → Published articles only
# Article.published.popular() → Published articles sorted by popularityCustom Managers volgen het DRY-principe en centraliseren de querylogica.
Klaar om je Django gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Views en URL's
Vraag 7: Wanneer gebruik je Class-Based Views versus Function-Based Views?
Function-Based Views (FBV's) bieden eenvoud en expliciete controle. Class-Based Views (CBV's) brengen herbruikbaarheid en structuur via overerving.
# views_comparison.py
# FBV: Explicit, simple, easy to understand
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.published.all()
return render(request, "articles/list.html", {"articles": articles})
# POST: article creation
form = ArticleForm(request.POST)
if form.is_valid():
form.save()
return redirect("article-list")
return render(request, "articles/list.html", {"form": form})# views_cbv.py
# CBV: Reusable, extensible via mixins
from django.views.generic import ListView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
class ArticleListView(LoginRequiredMixin, ListView):
model = Article
template_name = "articles/list.html"
context_object_name = "articles" # Variable name in the template
paginate_by = 20 # Automatic pagination
def get_queryset(self):
# Override to filter published articles
return Article.published.all()
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
form_class = ArticleForm
success_url = reverse_lazy("article-list")
def form_valid(self, form):
form.instance.author = self.request.user # Assign the author
return super().form_valid(form)Praktische regel: gebruik FBV's voor eenvoudige of niet-standaard logica, CBV's voor standaard CRUD-operaties.
Vraag 8: Hoe werken middlewares in Django?
Middlewares zijn hooks die elke aanvraag/antwoord verwerken. Elke middleware kan ingrijpen in verschillende fasen van de verwerkingscyclus.
# auth_middleware.py
# Custom authentication middleware
import jwt
from django.conf import settings
from django.http import JsonResponse
class JWTAuthenticationMiddleware:
"""Verifies JWT token on protected endpoints."""
EXEMPT_PATHS = ["/api/auth/login", "/api/auth/register", "/health"]
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Skip exempt paths
if any(request.path.startswith(p) for p in self.EXEMPT_PATHS):
return self.get_response(request)
# Extract and verify the token
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return JsonResponse({"error": "Missing token"}, status=401)
try:
token = auth_header.split(" ")[1]
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
request.user_id = payload["user_id"] # Attach to request
except jwt.ExpiredSignatureError:
return JsonResponse({"error": "Token expired"}, status=401)
except jwt.InvalidTokenError:
return JsonResponse({"error": "Invalid token"}, status=401)
return self.get_response(request)De volgorde van middlewares in MIDDLEWARE is cruciaal: ze worden van boven naar beneden uitgevoerd voor aanvragen en van beneden naar boven voor antwoorden.
Django REST Framework
Vraag 9: Wat is het verschil tussen Serializer en ModelSerializer?
Serializer definieert elk veld handmatig, terwijl ModelSerializer automatisch velden genereert vanuit het model.
# serializers.py
from rest_framework import serializers
# Manual Serializer: full control over each field
class ArticleSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=200)
content = serializers.CharField()
author_name = serializers.SerializerMethodField()
def get_author_name(self, obj):
return obj.author.get_full_name()
def create(self, validated_data):
return Article.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get("title", instance.title)
instance.content = validated_data.get("content", instance.content)
instance.save()
return instance
# ModelSerializer: automatic field generation
class ArticleModelSerializer(serializers.ModelSerializer):
author_name = serializers.SerializerMethodField()
comment_count = serializers.IntegerField(read_only=True)
class Meta:
model = Article
fields = ["id", "title", "content", "author", "author_name",
"comment_count", "published_at"]
read_only_fields = ["published_at"]
def get_author_name(self, obj):
return obj.author.get_full_name()Gebruik bij voorkeur ModelSerializer voor standaardgevallen en Serializer wanneer de representatie aanzienlijk verschilt van het model.
Vraag 10: Hoe implementeer je paginering in DRF?
DRF biedt verschillende pagineringsstrategieen die globaal of per view configureerbaar zijn.
# settings.py
# Global pagination configuration
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
}
# pagination.py
# Custom pagination per view
from rest_framework.pagination import CursorPagination, LimitOffsetPagination
class ArticleCursorPagination(CursorPagination):
"""Cursor pagination: performant for large datasets."""
page_size = 20
ordering = "-published_at" # Indexed field required
cursor_query_param = "cursor"
class ArticleLimitOffsetPagination(LimitOffsetPagination):
"""Offset/limit pagination: flexible but less performant."""
default_limit = 20
max_limit = 100
# views.py
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.published.all()
serializer_class = ArticleModelSerializer
pagination_class = ArticleCursorPagination # View-specific paginationCursor-paginering wordt aanbevolen voor grote datasets, aangezien deze performant blijft ongeacht het paginanummer, in tegenstelling tot OFFSET/LIMIT.
Vraag 11: Hoe beveilig je een API met DRF-permissies?
DRF biedt een modulair permissiesysteem dat authenticatie en granulaire autorisatie combineert.
# permissions.py
from rest_framework.permissions import BasePermission, IsAuthenticated
class IsAuthorOrReadOnly(BasePermission):
"""Only the author can modify, everyone can read."""
def has_object_permission(self, request, view, obj):
# GET, HEAD, OPTIONS are always allowed
if request.method in ("GET", "HEAD", "OPTIONS"):
return True
# Only the author can modify or delete
return obj.author == request.user
class IsAdminOrManager(BasePermission):
"""Access restricted to admins and managers."""
def has_permission(self, request, view):
return (
request.user.is_authenticated
and request.user.role in ("admin", "manager")
)
# views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.throttling import UserRateThrottle
class ArticleViewSet(ModelViewSet):
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
throttle_classes = [UserRateThrottle] # Rate limiting
def get_permissions(self):
# Dynamic permissions based on action
if self.action == "destroy":
return [IsAdminOrManager()]
return super().get_permissions()Combineer permission_classes op view-niveau en has_object_permission voor fijnmazige controle per object.
Signals en asynchrone taken
Vraag 12: Hoe werken Django Signals en wanneer gebruik je ze?
Signals maken het mogelijk om componenten te ontkoppelen door te reageren op gebeurtenissen van het framework of de applicatie.
# signals.py
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.core.mail import send_mail
@receiver(post_save, sender=Article)
def notify_on_publish(sender, instance, created, **kwargs):
"""Sends a notification when an article is published."""
if not created and instance.status == "published":
# Triggered only on publication (not creation)
subscribers = instance.author.subscribers.values_list("email", flat=True)
send_mail(
subject=f"New article: {instance.title}",
message=f"Check out the latest article by {instance.author.username}",
from_email="noreply@example.com",
recipient_list=list(subscribers),
)
@receiver(pre_delete, sender=Article)
def cleanup_article_files(sender, instance, **kwargs):
"""Deletes associated files before article deletion."""
if instance.cover_image:
instance.cover_image.delete(save=False) # Deletes the physical fileSignals zijn geschikt voor lichtgewicht neveneffecten (logging, cache-invalidatie). Voor zware taken verdient Celery de voorkeur.
Vraag 13: Hoe integreer je Celery met Django voor asynchrone taken?
Celery maakt de uitvoering van achtergrondtaken mogelijk, wat essentieel is voor langlopende operaties zoals het verzenden van e-mails of het verwerken van bestanden.
# celery_config.py
# Celery configuration in the Django project
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
app = Celery("myproject")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks() # Discovers tasks.py in each app
# tasks.py
from celery import shared_task
from django.core.mail import send_mass_mail
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def send_newsletter(self, article_id):
"""Sends newsletter asynchronously."""
try:
article = Article.objects.get(id=article_id)
subscribers = User.objects.filter(newsletter=True)
messages = [
(f"New: {article.title}", article.content[:200],
"noreply@example.com", [sub.email])
for sub in subscribers
]
send_mass_mail(messages, fail_silently=False)
except Article.DoesNotExist:
pass # Article was deleted in the meantime
except Exception as exc:
self.retry(exc=exc) # Automatic retry on error
# Calling from a view
# send_newsletter.delay(article.id) # Async execution
# send_newsletter.apply_async(args=[article.id], countdown=300) # 5-min delayCelery is in productie onmisbaar voor elke operatie die het HTTP-antwoord niet mag blokkeren.
Beveiliging en authenticatie
Vraag 14: Hoe beschermt Django tegen CSRF-aanvallen?
Django bevat een ingebouwde CSRF-beveiliging via een middleware die bij elke POST-aanvraag een uniek token verifieert.
# CSRF protection in forms
# The {% csrf_token %} template tag generates a hidden field
# For APIs (DRF), CSRF is often disabled in favor of tokens
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication", # Includes CSRF
"rest_framework.authentication.TokenAuthentication", # No CSRF
],
}
# For AJAX views with session auth
# The csrftoken cookie must be sent in the X-CSRFToken header# csrf_exemption.py
# Exempting a specific view (use with caution)
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
@ensure_csrf_cookie
def get_csrf_token(request):
"""Endpoint that forces sending the CSRF cookie to the client."""
return JsonResponse({"detail": "CSRF cookie set"})
@csrf_exempt # ⚠️ Use only for external webhooks
def stripe_webhook(request):
"""Stripe webhook - authenticated by signature, not CSRF."""
payload = request.body
sig_header = request.headers.get("Stripe-Signature")
# Verified by Stripe signature insteadCSRF mag nooit globaal worden uitgeschakeld. Gebruik csrf_exempt alleen op endpoints die via andere middelen worden geauthenticeerd (webhooks, API-tokens).
Vraag 15: Hoe implementeer je aangepaste authenticatie in Django?
Django maakt het mogelijk om het standaard User-model te vervangen en de authenticatie-backend aan te passen.
# models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
class CustomUserManager(BaseUserManager):
"""Manager for the custom User model."""
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError("Email is required")
email = self.normalize_email(email) # Normalizes the domain
user = self.model(email=email, **extra_fields)
user.set_password(password) # Hashes the password
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True) # Login by email
username = models.CharField(max_length=30, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
objects = CustomUserManager()
USERNAME_FIELD = "email" # Field used for login
REQUIRED_FIELDS = [] # Fields required in addition to USERNAME_FIELD
# settings.py
AUTH_USER_MODEL = "users.CustomUser" # Before the first migration!AUTH_USER_MODEL moet helemaal aan het begin van het project worden gedefinieerd. Het wijzigen na de eerste migraties is complex en risicovol.
Optimalisatie en performance
Vraag 16: Hoe optimaliseer je Django ORM-queries?
Query-optimalisatie is cruciaal voor de performance. Verschillende technieken verminderen het aantal en de kosten van SQL-queries.
# query_optimization.py
from django.db.models import F, Q, Count, Avg, Prefetch
# 1. Only/Defer: load only needed fields
articles = Article.objects.only("title", "published_at") # SELECT title, published_at
heavy_articles = Article.objects.defer("content") # Everything EXCEPT content
# 2. SQL-level aggregations (not Python)
stats = Article.objects.aggregate(
total=Count("id"),
avg_views=Avg("view_count"),
)
# 3. F() expressions: SQL-level operations
Article.objects.filter(published=True).update(
view_count=F("view_count") + 1 # Atomic SQL increment
)
# 4. Q() objects: complex queries
results = Article.objects.filter(
Q(title__icontains="django") | Q(tags__name="python"),
~Q(status="draft"), # NOT draft
published_at__year=2026
)
# 5. Bulk operations: reduce INSERT/UPDATE queries
articles = [Article(title=f"Article {i}") for i in range(100)]
Article.objects.bulk_create(articles, batch_size=50) # 2 queries instead of 100
Article.objects.filter(status="draft").update(status="archived") # 1 queryGebruik django-debug-toolbar tijdens de ontwikkeling om trage queries en N+1-problemen te identificeren.
Vraag 17: Hoe implementeer je caching in Django?
Django biedt een meerlaags caching-framework: per view, per template-fragment of voor willekeurige data.
# settings.py
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
}
}
# cache_strategies.py
from django.core.cache import cache
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
# Per-view cache: caches the entire HTTP response
@cache_page(60 * 15) # 15 minutes
def article_list(request):
return render(request, "articles/list.html", {"articles": Article.published.all()})
# Data cache: granular control
def get_popular_articles():
cache_key = "popular_articles_v1"
articles = cache.get(cache_key)
if articles is None:
articles = list(
Article.published.popular()[:10].values("id", "title", "view_count")
)
cache.set(cache_key, articles, timeout=60 * 30) # 30 min
return articles
# Cache invalidation
def invalidate_article_cache(article_id):
cache.delete(f"article_{article_id}")
cache.delete("popular_articles_v1")
cache.delete_pattern("article_list_*") # With django-redisRedis wordt aanbevolen als productie-cache-backend vanwege persistentie en geavanceerde functionaliteiten (patronen, TTL).
Klaar om je Django gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Migraties en databasebeheer
Vraag 18: Hoe ga je om met complexe migraties in Django?
Django-migraties beheren de evolutie van het databaseschema op een versie-gecontroleerde en reproduceerbare manier.
# 0005_migrate_data.py
# Custom data migration
from django.db import migrations
def migrate_user_roles(apps, schema_editor):
"""Converts is_admin booleans to text roles."""
User = apps.get_model("users", "CustomUser")
# Use apps.get_model() to access the historical model
User.objects.filter(is_admin=True).update(role="admin")
User.objects.filter(is_admin=False, is_staff=True).update(role="manager")
User.objects.filter(is_admin=False, is_staff=False).update(role="user")
def reverse_migrate(apps, schema_editor):
"""Reverse migration for rollback."""
User = apps.get_model("users", "CustomUser")
User.objects.filter(role="admin").update(is_admin=True)
class Migration(migrations.Migration):
dependencies = [
("users", "0004_add_role_field"),
]
operations = [
migrations.RunPython(migrate_user_roles, reverse_migrate),
]Voorzie altijd een reverse-functie om rollback mogelijk te maken. Test migraties op een kopie van de productiedatabase voor deployment.
Vraag 19: Hoe maak je aangepaste indexen voor optimalisatie?
Indexen versnellen veelgebruikte queries maar verhogen de schrijfkosten. Een zorgvuldige selectie is essentieel.
# models.py
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
status = models.CharField(max_length=20, db_index=True) # Simple index
published_at = models.DateTimeField(null=True)
author = models.ForeignKey("auth.User", on_delete=models.CASCADE)
class Meta:
indexes = [
# Composite index for frequent queries
models.Index(fields=["status", "-published_at"], name="idx_status_date"),
# Partial index: only published articles
models.Index(
fields=["published_at"],
condition=models.Q(status="published"),
name="idx_published_articles"
),
# GIN index for full-text search (PostgreSQL)
GinIndex(fields=["search_vector"], name="idx_search"),
]Samengestelde indexen volgen de kolomvolgorde: het meest selectieve veld dient als eerste te worden geplaatst.
Testen en kwaliteit
Vraag 20: Hoe structureer je tests in een Django-project?
Django biedt een robuust testframework gebaseerd op unittest, uitgebreid met pytest-django voor meer flexibiliteit.
# tests/test_views.py
from django.test import TestCase, Client
from django.urls import reverse
from rest_framework.test import APITestCase, APIClient
class ArticleViewTests(TestCase):
"""View tests with Django's test client."""
def setUp(self):
self.client = Client()
self.user = CustomUser.objects.create_user(
email="test@example.com", password="testpass123"
)
self.article = Article.objects.create(
title="Test Article",
content="Content here",
author=self.user,
status="published"
)
def test_article_list_returns_200(self):
response = self.client.get(reverse("article-list"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Test Article")
def test_create_article_requires_auth(self):
response = self.client.post(reverse("article-create"), {"title": "New"})
self.assertEqual(response.status_code, 302) # Redirects to login
class ArticleAPITests(APITestCase):
"""REST API tests with DRF."""
def setUp(self):
self.user = CustomUser.objects.create_user(
email="api@example.com", password="testpass123"
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_article_via_api(self):
data = {"title": "API Article", "content": "Created via API"}
response = self.client.post("/api/articles/", data, format="json")
self.assertEqual(response.status_code, 201)
self.assertEqual(Article.objects.count(), 1)Scheid tests in bestanden per domein: test_models.py, test_views.py, test_serializers.py, test_services.py.
Vraag 21: Hoe gebruik je fixtures en factories voor tests?
Factories (met factory_boy) hebben de voorkeur boven JSON-fixtures vanwege de flexibiliteit en onderhoudbaarheid van testdata.
# factories.py
import factory
from factory.django import DjangoModelFactory
class UserFactory(DjangoModelFactory):
class Meta:
model = CustomUser
email = factory.Sequence(lambda n: f"user{n}@example.com")
username = factory.Faker("user_name")
is_active = True
class ArticleFactory(DjangoModelFactory):
class Meta:
model = Article
title = factory.Faker("sentence", nb_words=5)
content = factory.Faker("paragraphs", nb=3)
author = factory.SubFactory(UserFactory) # Creates a user automatically
status = "published"
class Params:
draft = factory.Trait(status="draft", published_at=None)
# tests.py
def test_published_articles_count(self):
ArticleFactory.create_batch(5) # 5 published articles
ArticleFactory.create_batch(3, draft=True) # 3 drafts
self.assertEqual(Article.published.count(), 5)Factories zorgen voor consistente testdata en voorkomen afhankelijkheden tussen tests.
Geavanceerde patronen
Vraag 22: Hoe implementeer je WebSockets met Django Channels?
Django Channels breidt Django uit voorbij HTTP om WebSockets, realtime-protocollen en achtergrondtaken te ondersteunen.
# consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
"""WebSocket consumer for real-time chat."""
async def connect(self):
self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
self.room_group = f"chat_{self.room_name}"
# Join the room group
await self.channel_layer.group_add(self.room_group, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
# Leave the group
await self.channel_layer.group_discard(self.room_group, self.channel_name)
async def receive(self, text_data):
data = json.loads(text_data)
# Broadcast message to the entire group
await self.channel_layer.group_send(
self.room_group,
{"type": "chat.message", "message": data["message"],
"username": self.scope["user"].username}
)
async def chat_message(self, event):
# Send message to the WebSocket client
await self.send(text_data=json.dumps({
"message": event["message"],
"username": event["username"],
}))Django Channels gebruikt ASGI (in plaats van WSGI) en vereist een compatibele server zoals Daphne of Uvicorn.
Vraag 23: Leg het Repository- en Service Layer-patroon in Django uit
Het Service Layer-patroon scheidt de businesslogica van views en de ORM, wat testen en onderhoud vergemakkelijkt.
# services/article_service.py
from django.db import transaction
class ArticleService:
"""Service encapsulating article business logic."""
@staticmethod
def publish_article(article_id: int, user) -> Article:
"""Publishes an article with all validations."""
article = Article.objects.select_for_update().get(id=article_id)
if article.author != user:
raise PermissionError("Only the author can publish this article")
if article.status == "published":
raise ValueError("Article is already published")
article.status = "published"
article.published_at = timezone.now()
article.save(update_fields=["status", "published_at"])
# Side effects: notification, cache, analytics
send_newsletter.delay(article.id)
cache.delete("popular_articles_v1")
return article
@staticmethod
@transaction.atomic
def bulk_archive(article_ids: list[int], user) -> int:
"""Archives multiple articles in a transaction."""
updated = Article.objects.filter(
id__in=article_ids,
author=user,
status="published"
).update(status="archived", archived_at=timezone.now())
return updatedHet Service Layer is het toegangspunt voor alle businesslogica. Views en serializers delegeren naar de service, nooit rechtstreeks naar de ORM.
Vraag 24: Hoe beheer je omgevingsvariabelen en multi-omgevingsconfiguratie?
Configuratiebeheer volgt de 12-Factor App-principes: strikte scheiding tussen configuratie en code.
# settings/base.py
# Shared configuration across all environments
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv() # Loads the .env file
BASE_DIR = Path(__file__).resolve().parent.parent.parent
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] # Required, no default value
DEBUG = os.environ.get("DEBUG", "False").lower() == "true"
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("DB_NAME", "myapp"),
"USER": os.environ.get("DB_USER", "postgres"),
"HOST": os.environ.get("DB_HOST", "localhost"),
"PORT": os.environ.get("DB_PORT", "5432"),
}
}
# settings/production.py
from .base import *
DEBUG = False
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 yearCommit nooit geheimen in de code. Gebruik omgevingsvariabelen of een secrets manager (Vault, AWS Secrets Manager).
Vraag 25: Hoe deploy je een Django-applicatie in productie?
Productie-deployment vereist een uitgebreide checklist die beveiliging, performance en betrouwbaarheid omvat.
# Django deployment checklist
# 1. Built-in verification command
# python manage.py check --deploy
# 2. WSGI/ASGI configuration for production
# gunicorn.conf.py
import multiprocessing
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1 # Recommended formula
worker_class = "gthread" # Threaded workers
threads = 4
max_requests = 1000 # Recycle workers to avoid memory leaks
max_requests_jitter = 50
timeout = 30
accesslog = "-" # Logs to stdout
errorlog = "-"# docker-compose.yml (typical configuration)
# Services: web (gunicorn), db (postgres), redis (cache/celery), worker (celery)
# 3. Static files
# python manage.py collectstatic --noinput
# Serve via nginx or CDN (WhiteNoise for simple cases)
# 4. Nginx configuration
# - Proxy to gunicorn on port 8000
# - Serve /static/ and /media/ directly
# - Enable gzip, HTTP/2, and security headersVoer python manage.py check --deploy uit voor elke productie-release. Dit commando controleert de essientiele beveiligingsinstellingen.
Conclusie
Deze 25 vragen behandelen de essentiele aspecten van Django- en Python-sollicitatiegesprekken, van de basisprincipes van de MVT-architectuur tot productie-deploymentpatronen.
Voorbereidingschecklist:
- ✅ MVT-architectuur en request lifecycle
- ✅ ORM: QuerySets, N+1, select_related, prefetch_related
- ✅ Django REST Framework: serializers, paginering, permissies
- ✅ Beveiliging: CSRF, authenticatie, permissies
- ✅ Performance: ORM-optimalisatie, caching, indexen
- ✅ Testen: TestCase, APITestCase, factories
- ✅ Geavanceerde patronen: Channels, Service Layer, deployment
Elke vraag verdient een diepere verkenning met de officiele Django-documentatie. Interviewers waarderen kandidaten die de subtiliteiten van het framework begrijpen en hun technische beslissingen kunnen onderbouwen.
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Tags
Delen
Gerelateerde artikelen

Django 5: Een REST API bouwen met Django REST Framework
Volledige handleiding voor het bouwen van een professionele REST API met Django 5 en DRF. Serializers, ViewSets, JWT-authenticatie en best practices uitgelegd.

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.

Top 25 Data Science Sollicitatievragen in 2026 (met Antwoorden)
De meest gestelde data science sollicitatievragen van 2026, inclusief Python-codevoorbeelden, statistische concepten en machine learning-uitleg voor junior tot senior data scientists.