# Pertanyaan Wawancara Django: ORM, Middleware, dan DRF Secara Mendalam > Pertanyaan wawancara Django mencakup optimasi ORM dengan select_related dan prefetch_related, arsitektur middleware, serta performa serializer Django REST Framework, permissions, dan pola pagination. - Published: 2026-04-03 - Updated: 2026-04-03 - Author: SharpSkill - Tags: django, python, interview, orm, middleware, drf, rest-api - Reading time: 9 min --- Pertanyaan wawancara Django di tahun 2026 berfokus pada tiga pilar utama yang membedakan kandidat senior dari yang lain: penguasaan ORM, arsitektur middleware, dan pola desain Django REST Framework (DRF). Panduan ini menguraikan pertanyaan-pertanyaan yang sering diajukan oleh hiring manager, dilengkapi contoh kode siap produksi menggunakan Django 5.2 LTS dan DRF 3.17. > **Yang sebenarnya diuji pewawancara** > > Wawancara Django jarang menanyakan tentang CRUD dasar. Fokusnya telah bergeser ke optimasi QuerySet (select_related vs prefetch_related), desain middleware kustom, dan performa serializer DRF. Kandidat yang mampu menjelaskan masalah N+1 query dan menulis viewset yang efisien secara konsisten mengungguli kandidat yang hanya memahami generic views. ## Pertanyaan ORM Django: Optimasi QuerySet Pertanyaan ORM Django yang paling umum menyasar masalah N+1. Diberikan sebuah model dengan relasi foreign key, kandidat harus mendemonstrasikan kapan menggunakan `select_related` versus `prefetch_related`. ```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) ``` Perbedaan antara kedua metode tersebut terletak pada jenis relasi yang sedang diakses. ```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` melakukan SQL JOIN dan bekerja dengan ForeignKey serta OneToOneField. `prefetch_related` mengeksekusi query terpisah dan bekerja dengan ManyToManyField serta relasi reverse ForeignKey. Kesalahan penggunaan menyebabkan JOIN yang tidak perlu pada dataset besar atau masalah N+1 yang ditakuti. ## ORM Lanjutan: Custom Manager dan QuerySet Chaining Pewawancara sering meminta kandidat untuk menulis custom manager yang mengenkapsulasi logika bisnis. Tujuannya adalah memverifikasi bahwa kandidat memahami pola manager Django dan mampu menulis antarmuka query yang dapat digunakan kembali. ```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() ``` Pertanyaan lanjutan yang krusial: "Mengapa menggunakan custom QuerySet daripada hanya Manager?" Jawabannya adalah chaining. Method custom QuerySet dapat di-chain satu sama lain, sedangkan method Manager tidak dapat di-chain setelah pemanggilan pertama. ```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 5.2 LTS: Composite Primary Keys** > > Django 5.2 memperkenalkan CompositePrimaryKey, fitur yang telah lama dinantikan untuk model yang membutuhkan primary key multi-kolom. Pertanyaan wawancara tentang fitur ini semakin sering muncul, terutama bagi kandidat yang bekerja dengan database legacy atau skema data warehouse. ## Pertanyaan Middleware Django: Pipeline Request-Response Pertanyaan middleware menguji pemahaman kandidat tentang siklus hidup request-response Django. Pertanyaan standarnya: "Jelaskan urutan eksekusi middleware saat memproses request dan response." Jawabannya mengikuti pola yang ketat. Saat request masuk, kelas middleware dieksekusi dari atas ke bawah sesuai urutan di `MIDDLEWARE`. Saat response keluar, eksekusi berlangsung dari bawah ke atas. Arsitektur lapisan bawang ini berarti middleware pertama dalam daftar membungkus semuanya. ```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 ``` Pertanyaan lanjutan yang umum: "Bagaimana cara memotong rantai middleware?" Mengembalikan HttpResponse dari `__call__` sebelum memanggil `self.get_response(request)` menghentikan seluruh rantai. Middleware yang tersisa dan view tidak akan pernah dieksekusi. ```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) ``` ## Hook Middleware: process_view, process_exception, dan process_template_response Selain `__call__`, middleware Django mendukung tiga method hook opsional. Pewawancara menggunakan topik ini untuk mengukur kedalaman pengetahuan kandidat. ```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` dipanggil setelah resolusi URL tetapi sebelum view dieksekusi. Mengembalikan `None` melanjutkan eksekusi; mengembalikan `HttpResponse` memotong rantai. `process_exception` hanya dipanggil saat terjadi exception yang tidak tertangani. `process_template_response` hanya dipanggil untuk objek `TemplateResponse`, bukan `HttpResponse` biasa. ## Pertanyaan DRF: Performa Serializer Pertanyaan serializer [Django REST Framework](https://www.django-rest-framework.org/) berfokus pada serialisasi bersarang dan implikasi performa dari pendekatan yang berbeda. Pertanyaan yang paling sering diajukan: "Bagaimana menangani relasi bersarang tanpa menyebabkan N+1 query?" ```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"] ``` Serializer saja tidak menyelesaikan masalah performa. ViewSet harus mengoptimalkan queryset. ```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") ) ``` Tanpa `select_related` dan `prefetch_related` di ViewSet, setiap developer yang diserialisasi memicu query individual untuk company dan skills-nya. Pada daftar 50 developer, itu berarti 1 + 50 + 50 = 101 query, bukan 3. ## Custom Permissions dan Authentication DRF Pertanyaan DRF yang sering muncul: "Tulis custom permission yang membatasi akses berdasarkan role pengguna dan pemilik objek." ```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 ``` Nuansa yang dicari pewawancara: `has_permission` berjalan pada setiap request (level daftar), sedangkan `has_object_permission` hanya berjalan saat `get_object()` dipanggil (level detail). Lupa meng-override `get_queryset` untuk list view menciptakan celah keamanan di mana pengguna bisa melihat objek yang bukan milik mereka. > **Kesalahan keamanan DRF yang umum** > > Mengandalkan has_object_permission saja tanpa memfilter queryset membuat endpoint daftar tidak terlindungi. Selalu kombinasikan permission level objek dengan get_queryset yang difilter untuk menegakkan kontrol akses pada view daftar maupun detail. ## Pola Throttling dan Pagination DRF Throttling dan pagination adalah pertanyaan lanjutan yang standar. Pewawancara ingin melihat bahwa kandidat memahami konfigurasi API yang siap produksi. ```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 ``` Pagination berbasis cursor mengungguli offset pagination pada dataset besar karena tidak perlu menghitung total baris. Trade-off-nya: klien tidak bisa melompat ke halaman sembarang. Ini adalah jawaban yang diharapkan saat pewawancara bertanya "Mengapa memilih cursor pagination daripada page number pagination?" ## Kesimpulan Poin-poin utama untuk persiapan wawancara Django: - **Optimasi ORM**: Selalu sesuaikan metode query dengan jenis relasi. `select_related` untuk ForeignKey/OneToOne, `prefetch_related` untuk ManyToMany dan reverse FK. Custom QuerySet memungkinkan logika bisnis yang dapat di-chain dan digunakan kembali. - **Arsitektur middleware**: Request mengalir dari atas ke bawah, response dari bawah ke atas. Memotong rantai dengan mengembalikan response sebelum `get_response()` adalah pola fundamental untuk rate limiting, pengecekan autentikasi, dan validasi request. - **Performa serializer DRF**: Serializer bersarang memerlukan optimasi queryset secara eksplisit di ViewSet. Tanpa `select_related`/`prefetch_related`, serialisasi menyebabkan N+1 query pada skala besar. - **Permissions DRF**: Kombinasikan `has_object_permission` dengan `get_queryset` yang difilter untuk menegakkan kontrol akses pada endpoint daftar maupun detail. Permission level objek saja membuat list view tidak terlindungi. - **Throttling dan pagination**: Cursor pagination lebih scalable daripada offset pada tabel besar. Konfigurasikan throttle rate secara terpisah untuk pengguna anonim dan terautentikasi. Latih pola-pola ini dengan [pertanyaan wawancara Django ORM](/technologies/django/interview-questions/django-orm-advanced) dan [pertanyaan middleware Django](/technologies/django/interview-questions/django-middleware) di SharpSkill untuk membangun kepercayaan diri sebelum wawancara sesungguhnya. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/django/django-interview-orm-middleware-drf