# Preguntas de Entrevista Django y Python: Top 25 en 2026 > Las 25 preguntas mas frecuentes en entrevistas de Django y Python. ORM, vistas, middlewares, DRF, signals y optimizacion con respuestas detalladas y ejemplos de codigo. - Published: 2026-01-28 - Updated: 2026-04-12 - Author: SharpSkill - Tags: django, python, interview, django rest framework, technical interview - Reading time: 20 min --- Las entrevistas de Django evaluan el dominio del framework web mas popular de Python, el conocimiento del ORM, la arquitectura MVT y la capacidad de construir APIs REST robustas. Esta guia cubre las 25 preguntas mas frecuentes, desde los fundamentos de Django hasta patrones avanzados de Django REST Framework. > **Consejo para la entrevista** > > Los entrevistadores valoran a los candidatos que explican las decisiones arquitectonicas de Django. Comprender por que el framework adopta ciertas convenciones (convencion sobre configuracion) marca una diferencia real en las entrevistas. ## Fundamentos de Django ### Pregunta 1: Describir la arquitectura MVT de Django La arquitectura Model-View-Template (MVT) es la variacion de Django del patron MVC. El framework maneja la parte del controlador automaticamente, simplificando el desarrollo. ```python # models.py # The Model represents data structure and business logic from django.db import models class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() published_at = models.DateTimeField(auto_now_add=True) author = models.ForeignKey( "auth.User", on_delete=models.CASCADE, # Deletes articles when author is deleted related_name="articles" # Reverse access: user.articles.all() ) class Meta: ordering = ["-published_at"] # Default ordering def __str__(self): return self.title ``` ```python # views.py # The View contains request processing logic from django.shortcuts import render, get_object_or_404 def article_detail(request, pk): # get_object_or_404 raises Http404 if the object doesn't exist article = get_object_or_404(Article, pk=pk) return render(request, "blog/article_detail.html", {"article": article}) ``` En MVT, Django actua como controlador enrutando las URLs hacia las vistas apropiadas a traves de `urls.py`. El Template se encarga de la presentacion HTML. ### Pregunta 2: Cual es la diferencia entre un proyecto y una aplicacion en Django? Un **proyecto** es la configuracion general (settings, URLs raiz, WSGI/ASGI). Una **aplicacion** es un modulo reutilizable con una unica responsabilidad. Un proyecto contiene multiples aplicaciones. ```python # Creating a project and an app # django-admin startproject myproject # python manage.py startapp blog # settings.py # Registering apps in the project INSTALLED_APPS = [ "django.contrib.admin", # Admin interface "django.contrib.auth", # Authentication "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", # Custom apps "blog.apps.BlogConfig", # Blog app "users.apps.UsersConfig", # Users app ] ``` Cada aplicacion sigue el principio de responsabilidad unica y puede reutilizarse en diferentes proyectos. ### Pregunta 3: Explicar el ciclo de vida de una peticion en Django La peticion pasa por varias capas antes de llegar a la vista. Comprender este ciclo es esencial para la depuracion y la optimizacion. ```python # middleware.py # Middlewares intercept every request/response class RequestTimingMiddleware: """Middleware that measures processing time.""" def __init__(self, get_response): self.get_response = get_response # Reference to the next middleware def __call__(self, request): import time start = time.time() # Request phase: before the view response = self.get_response(request) # Response phase: after the view duration = time.time() - start response["X-Request-Duration"] = f"{duration:.3f}s" return response ``` El ciclo completo: peticion HTTP → WSGI/ASGI → middlewares (process_request) → resolutor de URLs → vista → middlewares (process_response) → respuesta HTTP. ## ORM y Base de Datos ### Pregunta 4: Como funciona el QuerySet de Django y que es la carga diferida? Los QuerySets se evaluan de forma diferida: ninguna consulta SQL se ejecuta hasta que los datos se utilizan realmente. ```python # queryset_lazy.py # Demonstrating QuerySet lazy loading # No SQL query executed here qs = Article.objects.filter(published=True) # No query qs = qs.exclude(title="Draft") # Still none qs = qs.order_by("-created_at") # Still none # The SQL query runs ONLY here for article in qs: # ONE combined SQL query print(article.title) # Other evaluation triggers list(qs) # Converting to list qs[0] # Index access len(qs) # Counting (prefer qs.count()) bool(qs) # Existence check (prefer qs.exists()) ``` Esta evaluacion diferida permite encadenar filtros sin sobrecarga, ejecutando una sola consulta optimizada. ### Pregunta 5: Que es el problema N+1 y como resolverlo? El problema N+1 ocurre cuando una consulta principal genera N consultas adicionales para cargar relaciones. Es la causa mas comun de lentitud en aplicaciones Django. ```python # n_plus_one.py # N+1 problem and solutions # ❌ PROBLEM: N+1 queries # 1 query for articles + 1 query PER article for the author articles = Article.objects.all() for article in articles: print(article.author.username) # SQL query on every iteration! # ✅ SOLUTION 1: select_related (ForeignKey, OneToOne) # Joins tables in ONE SQL query (JOIN) articles = Article.objects.select_related("author").all() for article in articles: print(article.author.username) # No additional query # ✅ SOLUTION 2: prefetch_related (ManyToMany, reverse FK) # Executes 2 separate queries + Python assembly articles = Article.objects.prefetch_related("tags").all() for article in articles: print(article.tags.all()) # Data already cached # ✅ SOLUTION 3: Custom Prefetch with filtering from django.db.models import Prefetch articles = Article.objects.prefetch_related( Prefetch( "comments", queryset=Comment.objects.filter(approved=True).select_related("user"), to_attr="approved_comments" # Custom attribute ) ) ``` Se utiliza `select_related` para relaciones ForeignKey/OneToOne (SQL JOIN) y `prefetch_related` para relaciones ManyToMany o inversas (consultas separadas). ### Pregunta 6: Como crear un Manager personalizado y cuando usarlo? Los Managers personalizados encapsulan consultas frecuentes a nivel del modelo, haciendo el codigo mas legible y reutilizable. ```python # managers.py # Custom Managers and QuerySets class PublishedQuerySet(models.QuerySet): """Reusable QuerySet for published articles.""" def published(self): return self.filter(status="published", published_at__lte=timezone.now()) def by_author(self, user): return self.filter(author=user) def popular(self): return self.annotate( comment_count=models.Count("comments") ).order_by("-comment_count") class PublishedManager(models.Manager): """Manager that exposes only published articles.""" def get_queryset(self): return PublishedQuerySet(self.model, using=self._db).published() class Article(models.Model): # ... objects = models.Manager() # Default manager (all articles) published = PublishedManager() # Custom manager (published only) # Usage: # Article.objects.all() → All articles # Article.published.all() → Published articles only # Article.published.popular() → Published articles sorted by popularity ``` Los Managers personalizados siguen el principio DRY y centralizan la logica de consultas. ## Vistas y URLs ### Pregunta 7: Cuando usar Class-Based Views vs Function-Based Views? Las Function-Based Views (FBVs) ofrecen simplicidad y control explicito. Las Class-Based Views (CBVs) aportan reutilizacion y estructura mediante herencia. ```python # views_comparison.py # FBV: Explicit, simple, easy to understand from django.http import JsonResponse from django.views.decorators.http import require_http_methods @require_http_methods(["GET", "POST"]) def article_list(request): if request.method == "GET": articles = Article.published.all() return render(request, "articles/list.html", {"articles": articles}) # POST: article creation form = ArticleForm(request.POST) if form.is_valid(): form.save() return redirect("article-list") return render(request, "articles/list.html", {"form": form}) ``` ```python # views_cbv.py # CBV: Reusable, extensible via mixins from django.views.generic import ListView, CreateView from django.contrib.auth.mixins import LoginRequiredMixin class ArticleListView(LoginRequiredMixin, ListView): model = Article template_name = "articles/list.html" context_object_name = "articles" # Variable name in the template paginate_by = 20 # Automatic pagination def get_queryset(self): # Override to filter published articles return Article.published.all() class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article form_class = ArticleForm success_url = reverse_lazy("article-list") def form_valid(self, form): form.instance.author = self.request.user # Assign the author return super().form_valid(form) ``` Regla practica: usar FBVs para logica simple o no estandar, CBVs para operaciones CRUD estandar. ### Pregunta 8: Como funcionan los middlewares en Django? Los middlewares son hooks que procesan cada peticion/respuesta. Cada middleware puede intervenir en diferentes fases del ciclo de procesamiento. ```python # auth_middleware.py # Custom authentication middleware import jwt from django.conf import settings from django.http import JsonResponse class JWTAuthenticationMiddleware: """Verifies JWT token on protected endpoints.""" EXEMPT_PATHS = ["/api/auth/login", "/api/auth/register", "/health"] def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # Skip exempt paths if any(request.path.startswith(p) for p in self.EXEMPT_PATHS): return self.get_response(request) # Extract and verify the token auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("Bearer "): return JsonResponse({"error": "Missing token"}, status=401) try: token = auth_header.split(" ")[1] payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"]) request.user_id = payload["user_id"] # Attach to request except jwt.ExpiredSignatureError: return JsonResponse({"error": "Token expired"}, status=401) except jwt.InvalidTokenError: return JsonResponse({"error": "Invalid token"}, status=401) return self.get_response(request) ``` El orden de los middlewares en `MIDDLEWARE` es crucial: se ejecutan de arriba hacia abajo para las peticiones y de abajo hacia arriba para las respuestas. ## Django REST Framework ### Pregunta 9: Cual es la diferencia entre Serializer y ModelSerializer? `Serializer` define manualmente cada campo, mientras que `ModelSerializer` genera automaticamente los campos a partir del modelo. ```python # serializers.py from rest_framework import serializers # Manual Serializer: full control over each field class ArticleSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=200) content = serializers.CharField() author_name = serializers.SerializerMethodField() def get_author_name(self, obj): return obj.author.get_full_name() def create(self, validated_data): return Article.objects.create(**validated_data) def update(self, instance, validated_data): instance.title = validated_data.get("title", instance.title) instance.content = validated_data.get("content", instance.content) instance.save() return instance # ModelSerializer: automatic field generation class ArticleModelSerializer(serializers.ModelSerializer): author_name = serializers.SerializerMethodField() comment_count = serializers.IntegerField(read_only=True) class Meta: model = Article fields = ["id", "title", "content", "author", "author_name", "comment_count", "published_at"] read_only_fields = ["published_at"] def get_author_name(self, obj): return obj.author.get_full_name() ``` Se recomienda `ModelSerializer` para casos estandar y `Serializer` cuando la representacion difiere significativamente del modelo. ### Pregunta 10: Como implementar paginacion en DRF? DRF ofrece varias estrategias de paginacion configurables globalmente o por vista. ```python # settings.py # Global pagination configuration REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", "PAGE_SIZE": 20, } # pagination.py # Custom pagination per view from rest_framework.pagination import CursorPagination, LimitOffsetPagination class ArticleCursorPagination(CursorPagination): """Cursor pagination: performant for large datasets.""" page_size = 20 ordering = "-published_at" # Indexed field required cursor_query_param = "cursor" class ArticleLimitOffsetPagination(LimitOffsetPagination): """Offset/limit pagination: flexible but less performant.""" default_limit = 20 max_limit = 100 # views.py class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.published.all() serializer_class = ArticleModelSerializer pagination_class = ArticleCursorPagination # View-specific pagination ``` La paginacion por cursor es recomendada para conjuntos de datos grandes ya que mantiene el rendimiento sin importar el numero de pagina, a diferencia de OFFSET/LIMIT. ### Pregunta 11: Como asegurar una API con permisos de DRF? DRF proporciona un sistema modular de permisos que combina autenticacion y autorizacion granular. ```python # permissions.py from rest_framework.permissions import BasePermission, IsAuthenticated class IsAuthorOrReadOnly(BasePermission): """Only the author can modify, everyone can read.""" def has_object_permission(self, request, view, obj): # GET, HEAD, OPTIONS are always allowed if request.method in ("GET", "HEAD", "OPTIONS"): return True # Only the author can modify or delete return obj.author == request.user class IsAdminOrManager(BasePermission): """Access restricted to admins and managers.""" def has_permission(self, request, view): return ( request.user.is_authenticated and request.user.role in ("admin", "manager") ) # views.py from rest_framework.viewsets import ModelViewSet from rest_framework.throttling import UserRateThrottle class ArticleViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsAuthorOrReadOnly] throttle_classes = [UserRateThrottle] # Rate limiting def get_permissions(self): # Dynamic permissions based on action if self.action == "destroy": return [IsAdminOrManager()] return super().get_permissions() ``` Se combinan `permission_classes` a nivel de vista y `has_object_permission` para control granular por objeto. ## Signals y Tareas Asincronas ### Pregunta 12: Como funcionan los signals de Django y cuando usarlos? Los signals permiten desacoplar componentes reaccionando a eventos del framework o de la aplicacion. ```python # signals.py from django.db.models.signals import post_save, pre_delete from django.dispatch import receiver from django.core.mail import send_mail @receiver(post_save, sender=Article) def notify_on_publish(sender, instance, created, **kwargs): """Sends a notification when an article is published.""" if not created and instance.status == "published": # Triggered only on publication (not creation) subscribers = instance.author.subscribers.values_list("email", flat=True) send_mail( subject=f"New article: {instance.title}", message=f"Check out the latest article by {instance.author.username}", from_email="noreply@example.com", recipient_list=list(subscribers), ) @receiver(pre_delete, sender=Article) def cleanup_article_files(sender, instance, **kwargs): """Deletes associated files before article deletion.""" if instance.cover_image: instance.cover_image.delete(save=False) # Deletes the physical file ``` Los signals son adecuados para efectos secundarios ligeros (logging, invalidacion de cache). Para tareas pesadas, se recomienda Celery. ### Pregunta 13: Como integrar Celery con Django para tareas asincronas? Celery permite la ejecucion de tareas en segundo plano, esencial para operaciones de larga duracion como envio de correos o procesamiento de archivos. ```python # celery_config.py # Celery configuration in the Django project import os from celery import Celery os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") app = Celery("myproject") app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() # Discovers tasks.py in each app # tasks.py from celery import shared_task from django.core.mail import send_mass_mail @shared_task(bind=True, max_retries=3, default_retry_delay=60) def send_newsletter(self, article_id): """Sends newsletter asynchronously.""" try: article = Article.objects.get(id=article_id) subscribers = User.objects.filter(newsletter=True) messages = [ (f"New: {article.title}", article.content[:200], "noreply@example.com", [sub.email]) for sub in subscribers ] send_mass_mail(messages, fail_silently=False) except Article.DoesNotExist: pass # Article was deleted in the meantime except Exception as exc: self.retry(exc=exc) # Automatic retry on error # Calling from a view # send_newsletter.delay(article.id) # Async execution # send_newsletter.apply_async(args=[article.id], countdown=300) # 5-min delay ``` Celery es esencial en produccion para cualquier operacion que no deba bloquear la respuesta HTTP. ## Seguridad y Autenticacion ### Pregunta 14: Como protege Django contra ataques CSRF? Django incluye proteccion CSRF integrada mediante un middleware que verifica un token unico en cada peticion POST. ```python # CSRF protection in forms # The {% csrf_token %} template tag generates a hidden field # For APIs (DRF), CSRF is often disabled in favor of tokens # settings.py REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework.authentication.SessionAuthentication", # Includes CSRF "rest_framework.authentication.TokenAuthentication", # No CSRF ], } # For AJAX views with session auth # The csrftoken cookie must be sent in the X-CSRFToken header ``` ```python # csrf_exemption.py # Exempting a specific view (use with caution) from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie @ensure_csrf_cookie def get_csrf_token(request): """Endpoint that forces sending the CSRF cookie to the client.""" return JsonResponse({"detail": "CSRF cookie set"}) @csrf_exempt # ⚠️ Use only for external webhooks def stripe_webhook(request): """Stripe webhook - authenticated by signature, not CSRF.""" payload = request.body sig_header = request.headers.get("Stripe-Signature") # Verified by Stripe signature instead ``` Nunca se debe deshabilitar CSRF globalmente. Se utiliza `csrf_exempt` unicamente en endpoints autenticados por otros medios (webhooks, tokens de API). ### Pregunta 15: Como implementar autenticacion personalizada en Django? Django permite reemplazar el modelo de User predeterminado y personalizar el backend de autenticacion. ```python # models.py from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class CustomUserManager(BaseUserManager): """Manager for the custom User model.""" def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError("Email is required") email = self.normalize_email(email) # Normalizes the domain user = self.model(email=email, **extra_fields) user.set_password(password) # Hashes the password user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault("is_staff", True) extra_fields.setdefault("is_superuser", True) return self.create_user(email, password, **extra_fields) class CustomUser(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) # Login by email username = models.CharField(max_length=30, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(auto_now_add=True) objects = CustomUserManager() USERNAME_FIELD = "email" # Field used for login REQUIRED_FIELDS = [] # Fields required in addition to USERNAME_FIELD # settings.py AUTH_USER_MODEL = "users.CustomUser" # Before the first migration! ``` Es necesario definir `AUTH_USER_MODEL` al inicio del proyecto. Cambiarlo despues de las migraciones iniciales es complejo y riesgoso. ## Optimizacion y Rendimiento ### Pregunta 16: Como optimizar las consultas del ORM de Django? La optimizacion de consultas es critica para el rendimiento. Varias tecnicas reducen el numero y costo de las consultas SQL. ```python # query_optimization.py from django.db.models import F, Q, Count, Avg, Prefetch # 1. Only/Defer: load only needed fields articles = Article.objects.only("title", "published_at") # SELECT title, published_at heavy_articles = Article.objects.defer("content") # Everything EXCEPT content # 2. SQL-level aggregations (not Python) stats = Article.objects.aggregate( total=Count("id"), avg_views=Avg("view_count"), ) # 3. F() expressions: SQL-level operations Article.objects.filter(published=True).update( view_count=F("view_count") + 1 # Atomic SQL increment ) # 4. Q() objects: complex queries results = Article.objects.filter( Q(title__icontains="django") | Q(tags__name="python"), ~Q(status="draft"), # NOT draft published_at__year=2026 ) # 5. Bulk operations: reduce INSERT/UPDATE queries articles = [Article(title=f"Article {i}") for i in range(100)] Article.objects.bulk_create(articles, batch_size=50) # 2 queries instead of 100 Article.objects.filter(status="draft").update(status="archived") # 1 query ``` Se recomienda usar `django-debug-toolbar` en desarrollo para identificar consultas lentas y problemas N+1. ### Pregunta 17: Como implementar cache en Django? Django proporciona un framework de cache multinivel: por vista, por fragmento de template o para datos arbitrarios. ```python # settings.py CACHES = { "default": { "BACKEND": "django.core.cache.backends.redis.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", } } # cache_strategies.py from django.core.cache import cache from django.views.decorators.cache import cache_page from django.utils.decorators import method_decorator # Per-view cache: caches the entire HTTP response @cache_page(60 * 15) # 15 minutes def article_list(request): return render(request, "articles/list.html", {"articles": Article.published.all()}) # Data cache: granular control def get_popular_articles(): cache_key = "popular_articles_v1" articles = cache.get(cache_key) if articles is None: articles = list( Article.published.popular()[:10].values("id", "title", "view_count") ) cache.set(cache_key, articles, timeout=60 * 30) # 30 min return articles # Cache invalidation def invalidate_article_cache(article_id): cache.delete(f"article_{article_id}") cache.delete("popular_articles_v1") cache.delete_pattern("article_list_*") # With django-redis ``` Se recomienda Redis como backend de cache en produccion por su persistencia y funcionalidades avanzadas (patrones, TTL). ## Migraciones y Gestion de Base de Datos ### Pregunta 18: Como manejar migraciones complejas en Django? Las migraciones de Django gestionan la evolucion del esquema de base de datos de forma versionada y reproducible. ```python # 0005_migrate_data.py # Custom data migration from django.db import migrations def migrate_user_roles(apps, schema_editor): """Converts is_admin booleans to text roles.""" User = apps.get_model("users", "CustomUser") # Use apps.get_model() to access the historical model User.objects.filter(is_admin=True).update(role="admin") User.objects.filter(is_admin=False, is_staff=True).update(role="manager") User.objects.filter(is_admin=False, is_staff=False).update(role="user") def reverse_migrate(apps, schema_editor): """Reverse migration for rollback.""" User = apps.get_model("users", "CustomUser") User.objects.filter(role="admin").update(is_admin=True) class Migration(migrations.Migration): dependencies = [ ("users", "0004_add_role_field"), ] operations = [ migrations.RunPython(migrate_user_roles, reverse_migrate), ] ``` Siempre se debe proporcionar una funcion `reverse` para permitir el rollback. Las migraciones deben probarse en una copia de la base de datos de produccion antes del despliegue. ### Pregunta 19: Como crear indices personalizados para optimizacion? Los indices aceleran las consultas frecuentes pero incrementan el costo de escritura. La seleccion cuidadosa es esencial. ```python # models.py class Article(models.Model): title = models.CharField(max_length=200) slug = models.SlugField(unique=True) status = models.CharField(max_length=20, db_index=True) # Simple index published_at = models.DateTimeField(null=True) author = models.ForeignKey("auth.User", on_delete=models.CASCADE) class Meta: indexes = [ # Composite index for frequent queries models.Index(fields=["status", "-published_at"], name="idx_status_date"), # Partial index: only published articles models.Index( fields=["published_at"], condition=models.Q(status="published"), name="idx_published_articles" ), # GIN index for full-text search (PostgreSQL) GinIndex(fields=["search_vector"], name="idx_search"), ] ``` Los indices compuestos siguen el orden de columnas: el campo mas selectivo debe ir primero. ## Testing y Calidad ### Pregunta 20: Como estructurar los tests en un proyecto Django? Django proporciona un framework de testing robusto basado en unittest, mejorado por pytest-django para mayor flexibilidad. ```python # tests/test_views.py from django.test import TestCase, Client from django.urls import reverse from rest_framework.test import APITestCase, APIClient class ArticleViewTests(TestCase): """View tests with Django's test client.""" def setUp(self): self.client = Client() self.user = CustomUser.objects.create_user( email="test@example.com", password="testpass123" ) self.article = Article.objects.create( title="Test Article", content="Content here", author=self.user, status="published" ) def test_article_list_returns_200(self): response = self.client.get(reverse("article-list")) self.assertEqual(response.status_code, 200) self.assertContains(response, "Test Article") def test_create_article_requires_auth(self): response = self.client.post(reverse("article-create"), {"title": "New"}) self.assertEqual(response.status_code, 302) # Redirects to login class ArticleAPITests(APITestCase): """REST API tests with DRF.""" def setUp(self): self.user = CustomUser.objects.create_user( email="api@example.com", password="testpass123" ) self.client = APIClient() self.client.force_authenticate(user=self.user) def test_create_article_via_api(self): data = {"title": "API Article", "content": "Created via API"} response = self.client.post("/api/articles/", data, format="json") self.assertEqual(response.status_code, 201) self.assertEqual(Article.objects.count(), 1) ``` Los tests deben separarse en archivos por dominio: `test_models.py`, `test_views.py`, `test_serializers.py`, `test_services.py`. ### Pregunta 21: Como usar fixtures y factories para tests? Las factories (con factory_boy) son preferibles a los fixtures JSON por su flexibilidad y mantenibilidad de datos de prueba. ```python # factories.py import factory from factory.django import DjangoModelFactory class UserFactory(DjangoModelFactory): class Meta: model = CustomUser email = factory.Sequence(lambda n: f"user{n}@example.com") username = factory.Faker("user_name") is_active = True class ArticleFactory(DjangoModelFactory): class Meta: model = Article title = factory.Faker("sentence", nb_words=5) content = factory.Faker("paragraphs", nb=3) author = factory.SubFactory(UserFactory) # Creates a user automatically status = "published" class Params: draft = factory.Trait(status="draft", published_at=None) # tests.py def test_published_articles_count(self): ArticleFactory.create_batch(5) # 5 published articles ArticleFactory.create_batch(3, draft=True) # 3 drafts self.assertEqual(Article.published.count(), 5) ``` Las factories aseguran datos de prueba consistentes y evitan dependencias entre tests. ## Patrones Avanzados ### Pregunta 22: Como implementar WebSockets con Django Channels? Django Channels extiende Django mas alla de HTTP para soportar WebSockets, protocolos en tiempo real y tareas en segundo plano. ```python # consumers.py import json from channels.generic.websocket import AsyncWebsocketConsumer class ChatConsumer(AsyncWebsocketConsumer): """WebSocket consumer for real-time chat.""" async def connect(self): self.room_name = self.scope["url_route"]["kwargs"]["room_name"] self.room_group = f"chat_{self.room_name}" # Join the room group await self.channel_layer.group_add(self.room_group, self.channel_name) await self.accept() async def disconnect(self, close_code): # Leave the group await self.channel_layer.group_discard(self.room_group, self.channel_name) async def receive(self, text_data): data = json.loads(text_data) # Broadcast message to the entire group await self.channel_layer.group_send( self.room_group, {"type": "chat.message", "message": data["message"], "username": self.scope["user"].username} ) async def chat_message(self, event): # Send message to the WebSocket client await self.send(text_data=json.dumps({ "message": event["message"], "username": event["username"], })) ``` Django Channels utiliza ASGI (en lugar de WSGI) y requiere un servidor compatible como Daphne o Uvicorn. ### Pregunta 23: Explicar el patron Repository y Service Layer en Django El patron Service Layer separa la logica de negocio de las vistas y el ORM, facilitando las pruebas y el mantenimiento. ```python # services/article_service.py from django.db import transaction class ArticleService: """Service encapsulating article business logic.""" @staticmethod def publish_article(article_id: int, user) -> Article: """Publishes an article with all validations.""" article = Article.objects.select_for_update().get(id=article_id) if article.author != user: raise PermissionError("Only the author can publish this article") if article.status == "published": raise ValueError("Article is already published") article.status = "published" article.published_at = timezone.now() article.save(update_fields=["status", "published_at"]) # Side effects: notification, cache, analytics send_newsletter.delay(article.id) cache.delete("popular_articles_v1") return article @staticmethod @transaction.atomic def bulk_archive(article_ids: list[int], user) -> int: """Archives multiple articles in a transaction.""" updated = Article.objects.filter( id__in=article_ids, author=user, status="published" ).update(status="archived", archived_at=timezone.now()) return updated ``` El Service Layer es el punto de entrada para toda la logica de negocio. Las vistas y serializers delegan al servicio, nunca directamente al ORM. ### Pregunta 24: Como gestionar variables de entorno y configuracion multi-ambiente? La gestion de configuracion sigue los principios de 12-Factor App: separacion estricta entre configuracion y codigo. ```python # settings/base.py # Shared configuration across all environments import os from pathlib import Path from dotenv import load_dotenv load_dotenv() # Loads the .env file BASE_DIR = Path(__file__).resolve().parent.parent.parent SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] # Required, no default value DEBUG = os.environ.get("DEBUG", "False").lower() == "true" ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",") DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.environ.get("DB_NAME", "myapp"), "USER": os.environ.get("DB_USER", "postgres"), "HOST": os.environ.get("DB_HOST", "localhost"), "PORT": os.environ.get("DB_PORT", "5432"), } } # settings/production.py from .base import * DEBUG = False SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS = 31536000 # 1 year ``` Nunca se deben incluir secretos en el codigo. Se utilizan variables de entorno o un gestor de secretos (Vault, AWS Secrets Manager). ### Pregunta 25: Como desplegar una aplicacion Django en produccion? El despliegue en produccion requiere una lista de verificacion completa que abarca seguridad, rendimiento y confiabilidad. ```python # Django deployment checklist # 1. Built-in verification command # python manage.py check --deploy # 2. WSGI/ASGI configuration for production # gunicorn.conf.py import multiprocessing bind = "0.0.0.0:8000" workers = multiprocessing.cpu_count() * 2 + 1 # Recommended formula worker_class = "gthread" # Threaded workers threads = 4 max_requests = 1000 # Recycle workers to avoid memory leaks max_requests_jitter = 50 timeout = 30 accesslog = "-" # Logs to stdout errorlog = "-" ``` ```python # docker-compose.yml (typical configuration) # Services: web (gunicorn), db (postgres), redis (cache/celery), worker (celery) # 3. Static files # python manage.py collectstatic --noinput # Serve via nginx or CDN (WhiteNoise for simple cases) # 4. Nginx configuration # - Proxy to gunicorn on port 8000 # - Serve /static/ and /media/ directly # - Enable gzip, HTTP/2, and security headers ``` Se debe ejecutar `python manage.py check --deploy` antes de cada release a produccion. Este comando verifica configuraciones de seguridad esenciales. ## Conclusion Estas 25 preguntas cubren los aspectos esenciales de las entrevistas de Django y Python, desde los fundamentos de la arquitectura MVT hasta los patrones de despliegue en produccion. Lista de preparacion: - ✅ Arquitectura MVT y ciclo de vida de peticiones - ✅ ORM: QuerySets, N+1, select_related, prefetch_related - ✅ Django REST Framework: serializers, paginacion, permisos - ✅ Seguridad: CSRF, autenticacion, permisos - ✅ Rendimiento: optimizacion del ORM, cache, indices - ✅ Testing: TestCase, APITestCase, factories - ✅ Patrones avanzados: Channels, Service Layer, despliegue > **Para profundizar** > > Cada pregunta merece una exploracion mas detallada con la documentacion oficial de Django. Los entrevistadores valoran a los candidatos que comprenden las sutilezas del framework y pueden justificar sus decisiones tecnicas. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/django/django-python-interview-questions