Pertanyaan Wawancara Django: ORM, Middleware, dan DRF Secara Mendalam

Pertanyaan wawancara Django mencakup optimasi ORM dengan select_related, prefetch_related, dan mode FETCH_PEERS Django 6.1, arsitektur middleware, serta performa serializer Django REST Framework, permissions, dan pola pagination.

Persiapan wawancara Django mencakup ORM, Middleware, dan konsep Django REST Framework

Pertanyaan wawancara Django mencakup tiga pilar 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 di tahun 2026, dilengkapi contoh kode siap produksi menggunakan Django 6.1 dan DRF 3.18.

Yang sebenarnya diuji pewawancara

Wawancara Django jarang menanyakan tentang CRUD dasar. Fokusnya telah bergeser ke optimasi QuerySet (select_related vs prefetch_related, mode FETCH_PEERS yang baru), 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.

Django 6.1: Mode FETCH_PEERS untuk Pencegahan N+1 Otomatis

Django 6.1 memperkenalkan sistem fetch mode baru yang mengatasi masalah N+1 query pada level ORM. Saat melakukan iterasi pada queryset dan mengakses field yang berelasi, FETCH_PEERS mengambil field tersebut untuk semua instance dalam queryset dengan satu query tambahan.

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

Tiga fetch mode tersedia:

  • FETCH_ONE (default): Mengambil field yang hilang hanya untuk instance saat ini
  • FETCH_PEERS: Mengambil field untuk semua instance dari QuerySet yang sama
  • FETCH_RAISE: Memunculkan FieldFetchBlocked untuk kode yang kritis terhadap performa di mana lazy loading harus dicegah

Untuk keperluan wawancara, kandidat harus mengetahui kedua pendekatan: select_related/prefetch_related eksplisit untuk kontrol yang lebih detail, dan FETCH_PEERS untuk optimasi otomatis saat melakukan iterasi pada queryset.

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 6.0: CompositePrimaryKey dan Background Tasks

Django 6.0 memperkenalkan CompositePrimaryKey untuk model yang membutuhkan primary key multi-kolom, serta framework background tasks bawaan. Pertanyaan wawancara tentang fitur-fitur ini semakin umum, terutama bagi kandidat yang bekerja dengan database legacy atau kebutuhan pemrosesan async.

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.

Siap menguasai wawancara Django Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Pertanyaan DRF: Performa Serializer

Pertanyaan serializer Django REST Framework 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?"

Sumber Referensi

Poin Penting 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. Mode FETCH_PEERS Django 6.1 menyediakan pencegahan N+1 otomatis saat melakukan iterasi pada queryset.
  • Custom QuerySets: Memungkinkan logika bisnis yang dapat di-chain dan digunakan kembali. Method custom Manager tidak dapat di-chain setelah pemanggilan pertama, tetapi method QuerySet bisa.
  • 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 dan pertanyaan middleware Django di SharpSkill untuk membangun kepercayaan diri sebelum wawancara sesungguhnya.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tantangan harian

Bisakah kamu menemukan bug di Django?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 13 September 2026

Tag

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

Bagikan

Artikel terkait