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.

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

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.

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?"

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

Tag

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

Bagikan

Artikel terkait