Preguntas de entrevista Django: ORM, Middleware y DRF a fondo
Preguntas de entrevista Django sobre optimización del ORM con select_related, prefetch_related y el modo FETCH_PEERS de Django 6.1, arquitectura de middleware y rendimiento de serializers en Django REST Framework, permisos y paginación.

Las preguntas de entrevista sobre Django giran en torno a tres pilares que diferencian a los candidatos con experiencia del resto: el dominio del ORM, la arquitectura de middleware y los patrones de diseño de Django REST Framework (DRF). Esta guía desglosa las preguntas exactas que formulan los reclutadores en 2026, con ejemplos de código listos para producción utilizando Django 6.1 y DRF 3.18.
Las entrevistas de Django rara vez preguntan sobre CRUD básico. El enfoque se ha trasladado a la optimización de QuerySets (select_related vs prefetch_related, el nuevo modo FETCH_PEERS), el diseño de middleware personalizado y el rendimiento de los serializers DRF. Los candidatos que pueden explicar las consultas N+1 y escribir viewsets eficientes superan constantemente a quienes solo conocen las vistas genéricas.
Preguntas de entrevista sobre el ORM de Django: optimización de QuerySets
La pregunta más común sobre el ORM de Django apunta al problema N+1. Dado un modelo con relaciones de clave foránea, el candidato debe demostrar cuándo utilizar select_related en lugar de prefetch_related.
# 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 diferencia entre ambos métodos radica en el tipo de relación que se recorre.
# queries.py — Correct approach for ForeignKey (single object)
developers = Developer.objects.select_related("company").all()
# Genera UNA sola consulta SQL con JOIN
# SELECT developer.*, company.* FROM developer INNER JOIN company ...
# Correct approach for ManyToMany (multiple objects)
developers = Developer.objects.prefetch_related("skills").all()
# Genera DOS consultas SQL:
# 1. SELECT * FROM developer
# 2. SELECT * FROM skill INNER JOIN developer_skills WHERE developer_id IN (...)select_related realiza un JOIN SQL y funciona con campos ForeignKey y OneToOneField. prefetch_related ejecuta una consulta separada y funciona con ManyToManyField y relaciones ForeignKey inversas. Confundir ambos provoca JOINs innecesarios en conjuntos de datos grandes o el temido patrón N+1.
Django 6.1: modo FETCH_PEERS para prevención automática del N+1
Django 6.1 introduce un nuevo sistema de modos de obtención que aborda las consultas N+1 a nivel del ORM. Al iterar sobre un queryset y acceder a un campo relacionado, FETCH_PEERS obtiene ese campo para todas las instancias del queryset con una única consulta adicional.
# 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 authorsHay tres modos de obtención disponibles:
FETCH_ONE(predeterminado): obtiene el campo faltante solo para la instancia actualFETCH_PEERS: obtiene el campo para todas las instancias del mismo QuerySetFETCH_RAISE: lanzaFieldFetchBlockedpara código crítico de rendimiento donde se debe prevenir la carga diferida
Para propósitos de entrevista, los candidatos deben conocer ambos enfoques: select_related/prefetch_related explícito para control granular, y FETCH_PEERS para optimización automática al iterar sobre querysets.
ORM avanzado: Managers personalizados y encadenamiento de QuerySets
Los entrevistadores suelen pedir a los candidatos que escriban un manager personalizado que encapsule la lógica de negocio. El objetivo es verificar que el candidato comprende el patrón manager de Django y puede escribir interfaces de consulta reutilizables.
# 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 pregunta de seguimiento clave: "¿Por qué usar un QuerySet personalizado en lugar de solo un Manager?" La respuesta está en el encadenamiento. Los métodos de un QuerySet personalizado pueden encadenarse, mientras que los métodos de un Manager no pueden encadenarse después de la primera llamada.
# usage.py — QuerySet chaining in action
# Esto funciona porque cada método retorna un QuerySet
senior_python_devs = (
Developer.active_objects # manager personalizado
.active() # método ActiveDeveloperQuerySet
.senior() # encadena otro método QuerySet
.by_skill("Python") # encadena un tercer método
.select_related("company") # los métodos estándar de QuerySet siguen funcionando
)Django 6.0 introdujo CompositePrimaryKey para modelos que necesitan claves primarias de múltiples columnas, además de un framework de tareas en segundo plano integrado. Las preguntas de entrevista sobre estas características son cada vez más comunes, especialmente para candidatos que trabajan con bases de datos heredadas o requisitos de procesamiento asíncrono.
Preguntas de entrevista sobre middleware Django: pipeline solicitud-respuesta
Las preguntas sobre middleware evalúan la comprensión del ciclo de vida solicitud-respuesta de Django. La pregunta estándar: "Explique el orden en que el middleware procesa una solicitud y una respuesta."
La respuesta sigue un patrón estricto. Durante una solicitud, las clases de middleware se ejecutan de arriba hacia abajo según se definen en MIDDLEWARE. Durante una respuesta, se ejecutan de abajo hacia arriba. Esta arquitectura de capas tipo cebolla significa que el primer middleware de la lista envuelve todo lo demás.
# 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 responseUna pregunta de seguimiento común: "¿Cómo se cortocircuita la cadena de middleware?" Devolver una HttpResponse desde __call__ antes de llamar a self.get_response(request) detiene la cadena por completo. Los middleware restantes y la vista nunca se ejecutan.
# 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)Hooks de middleware: process_view, process_exception y process_template_response
Más allá de __call__, el middleware de Django admite tres métodos hook opcionales. Los entrevistadores los utilizan para medir la profundidad del conocimiento.
# 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 se activa después de la resolución de URL pero antes de la vista. Devolver None continúa la ejecución; devolver una HttpResponse cortocircuita. process_exception se activa solo ante excepciones no controladas. process_template_response se activa solo para objetos TemplateResponse, no para HttpResponse regulares.
¿Listo para aprobar tus entrevistas de Django?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Preguntas de entrevista DRF: rendimiento de serializers
Las preguntas sobre serializers de Django REST Framework se centran en la serialización anidada y las implicaciones de rendimiento de los distintos enfoques. La pregunta más formulada: "¿Cómo se manejan las relaciones anidadas sin provocar consultas N+1?"
# 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"]El serializer por sí solo no resuelve el rendimiento. El ViewSet debe optimizar el queryset.
# 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")
)Sin select_related y prefetch_related en el ViewSet, cada desarrollador serializado dispara consultas individuales para su empresa y habilidades. En una lista de 50 desarrolladores, eso significa 1 + 50 + 50 = 101 consultas en lugar de 3.
Permisos y autenticación personalizados en DRF
Una pregunta frecuente en entrevistas sobre DRF: "Escriba un permiso personalizado que restrinja el acceso según el rol del usuario y el propietario del objeto."
# 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# 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 ownerEl matiz que buscan los entrevistadores: has_permission se ejecuta en cada solicitud (nivel lista), mientras que has_object_permission se ejecuta solo cuando se llama a get_object() (nivel detalle). Olvidar sobrescribir get_queryset para las vistas de lista crea una brecha de seguridad donde los usuarios pueden ver objetos que no les pertenecen.
Depender únicamente de has_object_permission sin filtrar el queryset deja el endpoint de lista desprotegido. Siempre se debe combinar los permisos a nivel de objeto con un get_queryset filtrado para aplicar el control de acceso tanto en las vistas de lista como de detalle.
Throttling y patrones de paginación en DRF
El throttling y la paginación son preguntas de seguimiento estándar. Los entrevistadores quieren ver que el candidato comprende la configuración de API lista para producción.
# 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,
}# 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=abc123La paginación por cursor supera a la paginación por offset en conjuntos de datos grandes porque no necesita contar el número total de filas. La contrapartida: los clientes no pueden saltar a una página arbitraria. Esta es la respuesta esperada cuando los entrevistadores preguntan "¿Por qué elegirían paginación por cursor en lugar de paginación por número de página?"
Fuentes
- Django 6.1 Release Notes: modo de obtención FETCH_PEERS para prevención automática del N+1
- Django 6.0 Release Notes: CompositePrimaryKey y framework Background Tasks
- Documentación Django REST Framework: patrones de Serializer y ViewSet
Puntos clave para la preparación de entrevistas Django
- Optimización del ORM: asociar el método de consulta al tipo de relación.
select_relatedpara ForeignKey/OneToOne,prefetch_relatedpara ManyToMany y FK inversa. El modoFETCH_PEERSde Django 6.1 proporciona prevención automática del N+1 al iterar sobre querysets. - QuerySets personalizados: permiten lógica de negocio encadenable y reutilizable. Los métodos de Manager personalizado no pueden encadenarse después de la primera llamada, pero los métodos de QuerySet sí.
- Arquitectura de middleware: la solicitud fluye de arriba hacia abajo, la respuesta de abajo hacia arriba. Cortocircuitar devolviendo una respuesta antes de
get_response()es un patrón fundamental para rate limiting, verificaciones de autenticación y validación de solicitudes. - Rendimiento de serializers DRF: los serializers anidados requieren optimización explícita del queryset en el ViewSet. Sin
select_related/prefetch_related, la serialización provoca consultas N+1 a escala. - Permisos DRF: combinar
has_object_permissioncon unget_querysetfiltrado para aplicar control de acceso en endpoints de lista y detalle. Los permisos a nivel de objeto solos dejan las vistas de lista sin protección. - Throttling y paginación: la paginación por cursor escala mejor que el offset en tablas grandes. Configurar tasas de throttling separadas para usuarios anónimos y autenticados.
Para afianzar estos conocimientos, se recomienda practicar con preguntas de entrevista sobre el ORM de Django y preguntas sobre middleware Django en SharpSkill antes de la entrevista real.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
¿Sabrías detectar el bug en Django?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 13 de septiembre de 2026
Etiquetas
Compartir
Artículos relacionados

Django 5.2: Middleware Personalizado y Manejo de Señales para Entrevistas Técnicas
Guía completa sobre middleware personalizado y señales en Django 5.2. Implementación de middleware de logging, middleware asíncrono, señales pre_save/post_save y preguntas frecuentes de entrevista.

Vistas asíncronas de Django y ASGI en 2026: rendimiento y preguntas de entrevista
Un análisis a fondo de las vistas asíncronas de Django y ASGI en 2026: cómo funcionan por dentro, qué servidor desplegar, el ORM asíncrono y la trampa de SynchronousOnlyOperation, además de preguntas de entrevista.

Django y PostgreSQL en 2026: indexación, búsqueda de texto completo y preguntas de entrevista
Guía práctica de optimización de Django con PostgreSQL: índices B-tree, parciales y de cobertura, búsqueda de texto completo con SearchVector y GIN, además de preguntas de entrevista 2026.