Domande Colloquio Django: ORM, Middleware e DRF in Profondità

Domande colloquio Django su ottimizzazione ORM con FETCH_PEERS, architettura middleware e performance serializer DRF. Esempi di codice con Django 6.1.

Preparazione colloquio Django su ORM, Middleware e Django REST Framework

Le domande colloquio Django si concentrano su tre pilastri fondamentali che distinguono i candidati senior dagli altri: la padronanza dell'ORM, l'architettura middleware e i design pattern di Django REST Framework (DRF). Questa guida analizza le domande esatte che i responsabili delle assunzioni pongono nel 2026, con esempi di codice pronti per la produzione utilizzando Django 6.1 e DRF 3.18.

Cosa valutano realmente i selezionatori

I colloqui Django raramente si soffermano sulle operazioni CRUD di base. L'attenzione si è spostata sull'ottimizzazione dei QuerySet (select_related vs prefetch_related, la nuova modalità FETCH_PEERS), sulla progettazione di middleware personalizzati e sulle performance dei serializer DRF. I candidati in grado di spiegare le query N+1 e scrivere viewset efficienti superano costantemente chi conosce solo le generic view.

Domande Colloquio Django ORM: Ottimizzazione dei QuerySet

La domanda più frequente sui colloqui Django ORM riguarda il problema N+1. Dato un modello con relazioni foreign key, i candidati devono dimostrare quando utilizzare select_related rispetto a 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)

La distinzione tra i due metodi si basa sul tipo di relazione attraversata.

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 esegue un JOIN SQL e funziona con ForeignKey e OneToOneField. prefetch_related esegue una query separata e funziona con ManyToManyField e relazioni ForeignKey inverse. Confonderli causa JOIN non necessari su dataset di grandi dimensioni o il temuto pattern N+1.

Django 6.1: Modalità FETCH_PEERS per la Prevenzione Automatica N+1

Django 6.1 introduce un nuovo sistema di fetch mode che affronta le query N+1 a livello ORM. Quando si itera su un queryset e si accede a un campo correlato, FETCH_PEERS carica quel campo per tutte le istanze nel queryset con una singola query aggiuntiva.

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

Sono disponibili tre modalità di fetch:

  • FETCH_ONE (predefinita): Carica il campo mancante solo per l'istanza corrente
  • FETCH_PEERS: Carica il campo per tutte le istanze dallo stesso QuerySet
  • FETCH_RAISE: Solleva FieldFetchBlocked per codice critico dove il lazy loading deve essere impedito

Ai fini del colloquio, i candidati dovrebbero conoscere entrambi gli approcci: select_related/prefetch_related espliciti per un controllo dettagliato e FETCH_PEERS per l'ottimizzazione automatica quando si itera sui queryset.

ORM Avanzato: Manager Personalizzati e Concatenamento QuerySet

I selezionatori spesso chiedono ai candidati di scrivere un manager personalizzato che incapsuli la logica di business. L'obiettivo è verificare che il candidato comprenda il pattern del manager Django e sappia scrivere interfacce di query riutilizzabili.

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()

La domanda di follow-up chiave: "Perché usare un QuerySet personalizzato invece di un semplice Manager?" La risposta è il concatenamento. I metodi dei QuerySet personalizzati possono essere concatenati tra loro, mentre i metodi del Manager non possono essere concatenati dopo la prima chiamata.

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 e Background Tasks

Django 6.0 ha introdotto CompositePrimaryKey per modelli che necessitano di chiavi primarie multi-colonna, più un framework integrato per i background task. Le domande su queste funzionalità stanno diventando più comuni, specialmente per candidati che lavorano con database legacy o requisiti di elaborazione asincrona.

Domande Colloquio Django Middleware: Pipeline Request-Response

Le domande sulla middleware testano la comprensione del candidato del ciclo di vita request-response di Django. La domanda standard: "Spiegare l'ordine in cui la middleware elabora una richiesta e una risposta."

La risposta segue uno schema rigoroso. Durante una richiesta, le classi middleware vengono eseguite dall'alto verso il basso come definito in MIDDLEWARE. Durante una risposta, vengono eseguite dal basso verso l'alto. Questa architettura a strati a cipolla significa che la prima middleware nella lista avvolge tutto.

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

Una domanda di follow-up comune: "Come cortocircuitare la catena middleware?" Restituire una HttpResponse da __call__ prima di chiamare self.get_response(request) interrompe completamente la catena. Le middleware rimanenti e la view non vengono mai eseguite.

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 del Middleware: process_view, process_exception e process_template_response

Oltre a __call__, la middleware Django supporta tre metodi hook opzionali. I selezionatori li usano per valutare la profondità delle conoscenze.

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 viene eseguito dopo la risoluzione dell'URL ma prima della view. Restituire None continua l'esecuzione; restituire una HttpResponse cortocircuita. process_exception viene eseguito solo per eccezioni non gestite. process_template_response viene eseguito solo per oggetti TemplateResponse, non per HttpResponse regolari.

Pronto a superare i tuoi colloqui su Django?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Domande Colloquio DRF: Performance dei Serializer

Le domande sui serializer di Django REST Framework si concentrano sulla serializzazione annidata e sulle implicazioni di performance dei diversi approcci. La domanda più frequente: "Come gestire le relazioni annidate senza causare query N+1?"

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

Il serializer da solo non risolve le performance. Il ViewSet deve ottimizzare il 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")
        )

Senza select_related e prefetch_related nel ViewSet, ogni sviluppatore serializzato attiva query individuali per la sua azienda e competenze. Su una lista di 50 sviluppatori, questo significa 1 + 50 + 50 = 101 query invece di 3.

DRF: Permessi Personalizzati e Autenticazione

Una domanda frequente nei colloqui DRF: "Scrivere un permesso personalizzato che limiti l'accesso in base al ruolo dell'utente e al proprietario dell'oggetto."

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

La sfumatura che i selezionatori cercano: has_permission viene eseguito su ogni richiesta (livello lista), mentre has_object_permission viene eseguito solo quando viene chiamato get_object() (livello dettaglio). Dimenticare di sovrascrivere get_queryset per le view lista crea una falla di sicurezza dove gli utenti possono vedere oggetti che non possiedono.

Errore comune di sicurezza DRF

Affidarsi esclusivamente a has_object_permission senza filtrare il queryset lascia l'endpoint lista non protetto. Combinare sempre i permessi object-level con un get_queryset filtrato per applicare il controllo accessi sia sulle view lista che su quelle di dettaglio.

DRF: Pattern di Throttling e Paginazione

Throttling e paginazione sono domande di follow-up standard. I selezionatori vogliono vedere che i candidati comprendano la configurazione API production-ready.

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

La paginazione basata su cursore supera la paginazione offset su dataset di grandi dimensioni perché non richiede il conteggio delle righe totali. Il compromesso: i client non possono saltare a pagine arbitrarie. Questa è la risposta attesa quando i selezionatori chiedono "Perché scegliere la paginazione cursor rispetto alla paginazione per numero di pagina?"

Fonti

Punti Chiave per la Preparazione ai Colloqui Django

  • Ottimizzazione ORM: Abbinare il metodo di query al tipo di relazione. select_related per ForeignKey/OneToOne, prefetch_related per ManyToMany e FK inverse. La modalità FETCH_PEERS di Django 6.1 fornisce prevenzione automatica N+1 quando si itera sui queryset.
  • QuerySet personalizzati: Abilitano logica di business concatenabile e riutilizzabile. I metodi del Manager personalizzato non possono essere concatenati dopo la prima chiamata, ma i metodi QuerySet sì.
  • Architettura middleware: La richiesta fluisce dall'alto verso il basso, la risposta dal basso verso l'alto. Cortocircuitare restituendo una risposta prima di get_response() è un pattern fondamentale per rate limiting, controlli auth e validazione delle richieste.
  • Performance serializer DRF: I serializer annidati richiedono ottimizzazione esplicita del queryset nel ViewSet. Senza select_related/prefetch_related, la serializzazione causa query N+1 su larga scala.
  • Permessi DRF: Combinare has_object_permission con un get_queryset filtrato per applicare il controllo accessi sia sugli endpoint lista che dettaglio. I permessi object-level da soli lasciano le view lista non protette.
  • Throttling e paginazione: La paginazione cursor scala meglio dell'offset su tabelle di grandi dimensioni. Configurare i rate di throttling separatamente per utenti anonimi e autenticati.

Esercitarsi con questi pattern usando domande colloquio Django ORM e domande Django middleware su SharpSkill per acquisire sicurezza prima del colloquio reale.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Sfida del giorno

Sapresti trovare il bug in Django?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 13 settembre 2026

Tag

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

Condividi

Articoli correlati