proyecto-django-docker/
├── docker-compose.yml # Orquestación de contenedores
├── Dockerfile # Imagen de Django
├── requirements.txt # Dependencias Python
├── .dockerignore # Archivos a ignorar
├── .env # Variables de entorno (opcional)
├── init.sh # Script de inicialización
├── manage.py # Django management
├── myproject/ # Configuración Django
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
└── users/ # App de usuarios
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── serializers.py
├── urls.py
├── views.py
└── migrations/
-
Docker Desktop instalado y corriendo
- Abre Docker Desktop
- Verifica que el ícono de Docker en la barra de tareas esté activo (ballena)
- Si dice "Docker Desktop is starting..." espera a que termine
-
Verificar instalación en PowerShell o CMD
docker --version
docker-compose --versionDeberías ver algo como:
Docker version 24.0.x
Docker Compose version v2.x.x
- Configuración recomendada en Docker Desktop
- Settings → Resources → Asigna al menos 4GB de RAM
- Settings → General → Activa "Use WSL 2 based engine" (recomendado)
- Settings → Docker Engine → Deja la configuración por defecto
- Visual Studio Code con extensiones:
- Docker (Microsoft)
- Python (Microsoft)
- Remote - Containers (Microsoft)
En PowerShell o CMD:
mkdir proyecto-django-docker
cd proyecto-django-dockerO usando el Explorador de Windows:
- Crea una carpeta llamada
proyecto-django-dockeren tu ubicación preferida - Abre esa carpeta en VS Code:
File → Open Folder - Abre la terminal integrada:
Terminal → New Terminal(Ctrl + Ñ)
Django==4.2.7
psycopg2-binary==2.9.9
djangorestframework==3.14.0FROM python:3.11-slim
# Variables de entorno para Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Directorio de trabajo
WORKDIR /app
# Instalar dependencias del sistema necesarias para PostgreSQL
RUN apt-get update && apt-get install -y \
postgresql-client \
gcc \
python3-dev \
musl-dev \
&& rm -rf /var/lib/apt/lists/*
# Copiar archivo de dependencias
COPY requirements.txt /app/
# Actualizar pip e instalar dependencias Python
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copiar todo el proyecto
COPY . /app/
# Exponer puerto 8000
EXPOSE 8000
# Comando por defecto (se sobrescribe en docker-compose)
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]__pycache__
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
pip-log.txt
pip-delete-this-directory.txt
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
*.sqlite3
db.sqlite3
*.db
.DS_Store
*.swp
*.swo
*~
.idea/
.vscode/
version: '3.8'
services:
# Servicio de Base de Datos PostgreSQL
db:
image: postgres:15-alpine
container_name: django_postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: django_db
POSTGRES_USER: django_user
POSTGRES_PASSWORD: django_pass
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U django_user -d django_db"]
interval: 5s
timeout: 5s
retries: 5
networks:
- django_network
# Servicio de Django
web:
build: .
container_name: django_web
command: sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
volumes:
- .:/app
ports:
- "8000:8000"
environment:
DB_NAME: django_db
DB_USER: django_user
DB_PASSWORD: django_pass
DB_HOST: db
DB_PORT: 5432
DJANGO_SETTINGS_MODULE: myproject.settings
depends_on:
db:
condition: service_healthy
networks:
- django_network
volumes:
postgres_data:
networks:
django_network:
driver: bridgeEn Windows, en lugar del script init.sh (que es para Linux/Mac), usaremos un archivo .bat o ejecutaremos los comandos directamente.
Opción 1: Crear init.bat (Windows)
@echo off
echo Iniciando proyecto Django con Docker...
echo Construyendo imagenes Docker...
docker-compose build
echo Verificando si manage.py existe...
if not exist "manage.py" (
echo Creando proyecto Django...
docker-compose run --rm web django-admin startproject myproject .
) else (
echo Proyecto Django ya existe
)
echo Verificando si app users existe...
if not exist "users\" (
echo Creando app de usuarios...
docker-compose run --rm web python manage.py startapp users
) else (
echo App users ya existe
)
echo.
echo Estructura base creada!
echo.
echo Proximos pasos:
echo 1. Configura settings.py segun la guia
echo 2. Crea los modelos, serializers y vistas
echo 3. Ejecuta: docker-compose up
pauseOpción 2: Ejecutar comandos manualmente (Recomendado para empezar)
En PowerShell o CMD, ejecuta uno por uno:
# Construir imágenes
docker-compose build
# Crear proyecto Django (solo primera vez)
docker-compose run --rm web django-admin startproject myproject .
# Crear app users (solo primera vez)
docker-compose run --rm web python manage.py startapp usersEn PowerShell, CMD o terminal de VS Code:
# Construir imágenes (esto instala las dependencias de requirements.txt)
docker-compose build
# Crear proyecto Django (solo primera vez)
docker-compose run --rm web django-admin startproject myproject .
# Crear app users (solo primera vez)
docker-compose run --rm web python manage.py startapp users- Los archivos creados por Docker pueden tener permisos diferentes
- Si no puedes editarlos, cierra VS Code y vuelve a abrir la carpeta
- Alternativamente, puedes crear los archivos manualmente y copiar el contenido
💡 Tip: Si usas Git Bash en Windows, puedes usar los comandos de Linux/Mac también.
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-cambiar-en-produccion'
DEBUG = True
ALLOWED_HOSTS = ['*']
# Aplicaciones instaladas
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third party
'rest_framework',
# Apps locales
'users',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'myproject.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'myproject.wsgi.application'
# 🔥 CONFIGURACIÓN DE BASE DE DATOS POSTGRESQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME', 'django_db'),
'USER': os.environ.get('DB_USER', 'django_user'),
'PASSWORD': os.environ.get('DB_PASSWORD', 'django_pass'),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
# Validación de contraseñas
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
# Internacionalización
LANGUAGE_CODE = 'es-pe'
TIME_ZONE = 'America/Lima'
USE_I18N = True
USE_TZ = True
# Archivos estáticos
STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# 🔥 MODELO DE USUARIO PERSONALIZADO
AUTH_USER_MODEL = 'users.CustomUser'
# 🔥 CONFIGURACIÓN DE REST FRAMEWORK
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
"""
Modelo de usuario personalizado con campos adicionales
"""
email = models.EmailField(unique=True, verbose_name='Email')
phone = models.CharField(max_length=20, blank=True, verbose_name='Teléfono')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Fecha de creación')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Fecha de actualización')
class Meta:
verbose_name = 'Usuario'
verbose_name_plural = 'Usuarios'
ordering = ['-created_at']
def __str__(self):
return self.usernamefrom rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import CustomUser
class UserSerializer(serializers.ModelSerializer):
"""Serializer para lectura de usuarios"""
class Meta:
model = CustomUser
fields = ['id', 'username', 'email', 'phone', 'first_name',
'last_name', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class UserCreateSerializer(serializers.ModelSerializer):
"""Serializer para creación de usuarios"""
password = serializers.CharField(
write_only=True,
required=True,
validators=[validate_password],
style={'input_type': 'password'}
)
password_confirm = serializers.CharField(
write_only=True,
required=True,
style={'input_type': 'password'}
)
class Meta:
model = CustomUser
fields = ['username', 'email', 'password', 'password_confirm',
'phone', 'first_name', 'last_name']
def validate(self, attrs):
if attrs['password'] != attrs['password_confirm']:
raise serializers.ValidationError({
"password": "Las contraseñas no coinciden"
})
return attrs
def create(self, validated_data):
validated_data.pop('password_confirm')
user = CustomUser.objects.create_user(**validated_data)
return user
class UserUpdateSerializer(serializers.ModelSerializer):
"""Serializer para actualización de usuarios"""
class Meta:
model = CustomUser
fields = ['email', 'phone', 'first_name', 'last_name']
class LoginSerializer(serializers.Serializer):
"""Serializer para login"""
username = serializers.CharField(required=True)
password = serializers.CharField(
required=True,
write_only=True,
style={'input_type': 'password'}
)
class ChangePasswordSerializer(serializers.Serializer):
"""Serializer para cambio de contraseña"""
old_password = serializers.CharField(required=True, write_only=True)
new_password = serializers.CharField(
required=True,
write_only=True,
validators=[validate_password]
)
new_password_confirm = serializers.CharField(required=True, write_only=True)
def validate(self, attrs):
if attrs['new_password'] != attrs['new_password_confirm']:
raise serializers.ValidationError({
"new_password": "Las contraseñas no coinciden"
})
return attrsfrom rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import authenticate, login, logout
from .models import CustomUser
from .serializers import (
UserSerializer,
UserCreateSerializer,
UserUpdateSerializer,
LoginSerializer,
ChangePasswordSerializer
)
class UserViewSet(viewsets.ModelViewSet):
"""
ViewSet para CRUD completo de usuarios
"""
queryset = CustomUser.objects.all()
def get_serializer_class(self):
if self.action == 'create':
return UserCreateSerializer
elif self.action in ['update', 'partial_update']:
return UserUpdateSerializer
return UserSerializer
def get_permissions(self):
# Login y registro son públicos
if self.action in ['create', 'login']:
return [AllowAny()]
# Todo lo demás requiere autenticación
return [IsAuthenticated()]
def create(self, request, *args, **kwargs):
"""Registrar nuevo usuario"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
'message': 'Usuario creado exitosamente',
'user': UserSerializer(user).data
}, status=status.HTTP_201_CREATED)
def list(self, request, *args, **kwargs):
"""Listar todos los usuarios"""
queryset = self.get_queryset()
serializer = UserSerializer(queryset, many=True)
return Response({
'count': queryset.count(),
'users': serializer.data
})
def retrieve(self, request, *args, **kwargs):
"""Obtener un usuario específico"""
instance = self.get_object()
serializer = UserSerializer(instance)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
"""Actualizar usuario completo"""
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({
'message': 'Usuario actualizado exitosamente',
'user': UserSerializer(instance).data
})
def destroy(self, request, *args, **kwargs):
"""Eliminar usuario"""
instance = self.get_object()
username = instance.username
instance.delete()
return Response({
'message': f'Usuario {username} eliminado exitosamente'
}, status=status.HTTP_200_OK)
@action(detail=False, methods=['post'], permission_classes=[AllowAny])
def login(self, request):
"""Login de usuario"""
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = authenticate(
username=serializer.validated_data['username'],
password=serializer.validated_data['password']
)
if user:
login(request, user)
return Response({
'message': 'Login exitoso',
'user': UserSerializer(user).data
})
return Response({
'error': 'Credenciales inválidas'
}, status=status.HTTP_401_UNAUTHORIZED)
@action(detail=False, methods=['post'])
def logout(self, request):
"""Logout de usuario"""
logout(request)
return Response({
'message': 'Logout exitoso'
})
@action(detail=False, methods=['get'])
def me(self, request):
"""Obtener perfil del usuario actual"""
serializer = UserSerializer(request.user)
return Response(serializer.data)
@action(detail=False, methods=['post'])
def change_password(self, request):
"""Cambiar contraseña del usuario actual"""
serializer = ChangePasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
# Verificar contraseña actual
if not user.check_password(serializer.validated_data['old_password']):
return Response({
'error': 'Contraseña actual incorrecta'
}, status=status.HTTP_400_BAD_REQUEST)
# Cambiar contraseña
user.set_password(serializer.validated_data['new_password'])
user.save()
return Response({
'message': 'Contraseña cambiada exitosamente'
})from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = [
path('', include(router.urls)),
]from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('users.urls')),
]from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
list_display = ['username', 'email', 'phone', 'is_staff', 'is_active', 'created_at']
list_filter = ['is_staff', 'is_active', 'created_at']
search_fields = ['username', 'email', 'phone']
ordering = ['-created_at']
fieldsets = UserAdmin.fieldsets + (
('Información Adicional', {
'fields': ('phone', 'created_at', 'updated_at')
}),
)
readonly_fields = ['created_at', 'updated_at']En PowerShell/CMD:
docker-compose run --rm web python manage.py makemigrations
docker-compose run --rm web python manage.py migratedocker-compose run --rm web python manage.py createsuperuserTe pedirá:
- Username
- Password (no se verá mientras escribes)
- Password confirmation
Modo normal (verás los logs en tiempo real):
docker-compose upModo detached (segundo plano):
docker-compose up -dPara detener:
- Modo normal:
Ctrl + Cen la terminal - Modo detached:
docker-compose down
Abre tu navegador en:
💡 Tip Windows: Si no carga, verifica:
- Docker Desktop está corriendo
- Los contenedores están activos:
docker-compose ps - Firewall de Windows no está bloqueando el puerto 8000
-
Ver la interfaz de Django REST Framework
- Abre: http://localhost:8000/api/users/
- Verás una interfaz web donde puedes hacer las peticiones
-
Crear usuario desde el navegador
- Ve a: http://localhost:8000/api/users/
- Completa el formulario en la parte inferior
- Click en "POST"
-
Login desde el navegador
- Ve a: http://localhost:8000/api/users/login/
- Ingresa username y password
- Click en "POST"
- PowerShell tiene su propio
Invoke-WebRequest(aliascurl) - O puedes instalar curl real desde: https://curl.se/windows/
- O usar Postman (recomendado para principiantes)
Con PowerShell nativo:
$body = @{
username = "juanperez"
email = "juan@example.com"
password = "MiPassword123!"
password_confirm = "MiPassword123!"
phone = "+51999888777"
first_name = "Juan"
last_name = "Pérez"
} | ConvertTo-Json
Invoke-WebRequest -Uri "http://localhost:8000/api/users/" `
-Method POST `
-ContentType "application/json" `
-Body $body$body = @{
username = "juanperez"
password = "MiPassword123!"
} | ConvertTo-Json
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
Invoke-WebRequest -Uri "http://localhost:8000/api/users/login/" `
-Method POST `
-ContentType "application/json" `
-Body $body `
-SessionVariable session- Descargar Postman: https://www.postman.com/downloads/
- Crear una colección llamada "Django Users API"
- Configurar requests:
POST - Crear usuario
- URL:
http://localhost:8000/api/users/ - Method: POST
- Body → raw → JSON:
{
"username": "juanperez",
"email": "juan@example.com",
"password": "MiPassword123!",
"password_confirm": "MiPassword123!",
"phone": "+51999888777",
"first_name": "Juan",
"last_name": "Pérez"
}POST - Login
- URL:
http://localhost:8000/api/users/login/ - Method: POST
- Body → raw → JSON:
{
"username": "juanperez",
"password": "MiPassword123!"
}- Ir a Tests y agregar (para guardar la sesión):
pm.cookies.jar();GET - Ver perfil
- URL:
http://localhost:8000/api/users/me/ - Method: GET
- (Debe estar en la misma sesión después del login)
Si instalaste curl de https://curl.se/windows/:
# Crear usuario
curl -X POST http://localhost:8000/api/users/ ^
-H "Content-Type: application/json" ^
-d "{\"username\":\"juanperez\",\"email\":\"juan@example.com\",\"password\":\"MiPassword123!\",\"password_confirm\":\"MiPassword123!\",\"phone\":\"+51999888777\",\"first_name\":\"Juan\",\"last_name\":\"Perez\"}"
# Login (guarda cookies)
curl -X POST http://localhost:8000/api/users/login/ ^
-H "Content-Type: application/json" ^
-c cookies.txt ^
-d "{\"username\":\"juanperez\",\"password\":\"MiPassword123!\"}"
# Ver perfil
curl -X GET http://localhost:8000/api/users/me/ ^
-b cookies.txtNota: En CMD de Windows usa ^ en lugar de \ para continuar líneas.
En PowerShell, CMD o terminal de VS Code:
# Ver contenedores activos
docker-compose ps
# Ver TODOS los contenedores (incluso detenidos)
docker ps -a
# Ver logs en tiempo real
docker-compose logs -f
# Ver logs solo de Django
docker-compose logs -f web
# Ver logs solo de PostgreSQL
docker-compose logs -f db
# Detener servicios (mantiene los datos)
docker-compose down
# Detener y eliminar volúmenes (⚠️ BORRA LA BASE DE DATOS)
docker-compose down -v
# Reiniciar servicios
docker-compose restart
# Reiniciar solo Django
docker-compose restart web
# Reconstruir imágenes (cuando cambias Dockerfile o requirements.txt)
docker-compose build --no-cache
# Ejecutar comandos en contenedor corriendo
docker-compose exec web python manage.py shell
# Acceder a bash del contenedor (para explorar)
docker-compose exec web bash
# O si bash no está disponible:
docker-compose exec web sh
# Conectar a PostgreSQL
docker-compose exec db psql -U django_user -d django_db
# Ver volúmenes creados
docker volume ls
# Ver imágenes descargadas
docker images
# Limpiar todo lo que no se usa (libera espacio)
docker system prune -a
# Ver uso de espacio en disco
docker system df# Ver estadísticas de recursos (CPU, RAM)
docker stats
# Inspeccionar un contenedor
docker inspect proyecto-django-docker-web-1
# Ver red creada
docker network ls
docker network inspect proyecto-django-docker_django_network
# Copiar archivo desde el contenedor a Windows
docker cp proyecto-django-docker-web-1:/app/manage.py ./manage_backup.py
# Copiar archivo desde Windows al contenedor
docker cp ./archivo.txt proyecto-django-docker-web-1:/app/Si necesitas liberar recursos:
- Click derecho en el ícono de Docker Desktop (ballena) en la bandeja
- Selecciona "Quit Docker Desktop"
- Para reiniciar, abre Docker Desktop desde el menú de inicio
| Método | Endpoint | Descripción | Auth |
|---|---|---|---|
| POST | /api/users/ |
Crear usuario | ❌ |
| GET | /api/users/ |
Listar usuarios | ✅ |
| GET | /api/users/{id}/ |
Ver usuario | ✅ |
| PUT | /api/users/{id}/ |
Actualizar (completo) | ✅ |
| PATCH | /api/users/{id}/ |
Actualizar (parcial) | ✅ |
| DELETE | /api/users/{id}/ |
Eliminar usuario | ✅ |
| POST | /api/users/login/ |
Login | ❌ |
| POST | /api/users/logout/ |
Logout | ✅ |
| GET | /api/users/me/ |
Perfil actual | ✅ |
| POST | /api/users/change_password/ |
Cambiar contraseña | ✅ |
Entorno tradicional (sin Docker):
Windows → Python global → venv → Django
Problema: Cada proyecto contamina tu Windows con dependencias
Con Docker:
Windows → Docker Desktop → Contenedor aislado → Python + Django
✅ Cada contenedor es un "mini-ordenador" aislado ✅ No ensucias tu Windows ✅ Mismo ambiente en todos lados (Windows, Mac, Linux)
FROM python:3.11-slim # Sistema base (como instalar Ubuntu)
WORKDIR /app # Carpeta de trabajo
COPY requirements.txt /app/ # Copiar lista de dependencias
RUN pip install -r requirements.txt # Instalar dependencias
COPY . /app/ # Copiar todo el código👉 Es como un "script de instalación" que crea una imagen (plantilla)
- Imagen: plantilla inmutable (como un .exe instalador)
- Contenedor: instancia corriendo (como un programa abierto)
docker-compose build # Crea la IMAGEN
docker-compose up # Crea y ejecuta CONTENEDORES desde la imagenCoordina múltiples contenedores:
services:
db: # Contenedor 1: PostgreSQL
web: # Contenedor 2: Django👉 Ambos corren simultáneamente y se comunican por red interna
Tipos:
a) Volumen named (para la base de datos):
volumes:
- postgres_data:/var/lib/postgresql/data👉 Datos persisten aunque borres el contenedor
👉 Docker los guarda en: C:\Users\TuUsuario\AppData\Local\Docker\wsl\data\
b) Bind mount (para código en desarrollo):
volumes:
- .:/app👉 Tu carpeta de Windows se "monta" dentro del contenedor 👉 Cambios en Windows = cambios en el contenedor (hot reload)
En Windows, verías:
- Django:
localhost:8000 - PostgreSQL:
localhost:5432
Dentro de Docker:
- Django se conecta a:
db:5432(nombre del servicio, no localhost) - Docker crea una red privada entre contenedores
environment:
DB_HOST: db # Django lee esto en settings.py
DB_USER: django_user👉 Configuración sin hardcodear valores 👉 Diferente por ambiente (desarrollo, producción)
healthcheck:
test: ["CMD-SHELL", "pg_isready -U django_user"]👉 Django espera a que PostgreSQL esté listo 👉 Evita errores de "connection refused"
Dockerfile ejecuta comandos en orden:
COPY requirements.txt /app/ # Capa 1 (cambia poco)
RUN pip install -r requirements.txt # Capa 2 (se cachea)
COPY . /app/ # Capa 3 (cambia mucho)👉 Si solo cambias código, Docker reutiliza capas 1-2 (build rápido) 👉 Si cambias requirements.txt, reconstruye desde capa 2
Foreground (normal):
docker-compose up👉 Ves logs en tiempo real 👉 Ctrl+C para detener
Detached (background):
docker-compose up -d👉 Corre en segundo plano 👉 Puedes cerrar la terminal
1. Escribes Dockerfile y docker-compose.yml
↓
2. docker-compose build
→ Descarga imagen base Python
→ Instala dependencias
→ Crea IMAGEN local
↓
3. docker-compose up
→ Crea red interna
→ Inicia contenedor PostgreSQL
→ Espera healthcheck
→ Inicia contenedor Django
→ Conecta ambos por red
↓
4. Accedes desde Windows:
→ localhost:8000 (puerto mapeado)
→ El resto es magia de Docker 🎩✨
Sin Docker en Windows:
# Instalar Python
# Crear venv
python -m venv venv
.\venv\Scripts\activate
# Instalar PostgreSQL nativo en Windows
# Configurar PostgreSQL
# Instalar dependencias
pip install -r requirements.txt
# Configurar variables de entorno
# Ejecutar
python manage.py runserver❌ Muchos pasos ❌ Puede fallar en cada paso ❌ Diferente en Mac/Linux
Con Docker en Windows:
docker-compose up✅ Un comando ✅ Funciona siempre igual ✅ Mismo comando en Mac/Linux/Windows
- "Funciona en mi máquina" → Funciona en todas
- Dependencias conflictivas → Cada proyecto aislado
- Instalar PostgreSQL → Ya viene en el contenedor
- Configurar Python → Ya viene configurado
- Compartir proyecto → Solo necesitas Docker
- Cambiar de PC → Llevas el proyecto completo
- Django Dev Server: http://localhost:8000
- Admin Panel: http://localhost:8000/admin/
- API Root: http://localhost:8000/api/users/
- PostgreSQL: localhost:5432
- User:
django_user - Pass:
django_pass - Database:
django_db
- User:
Opción 1: pgAdmin (Interfaz gráfica)
- Descargar: https://www.pgadmin.org/download/pgadmin-4-windows/
- Crear nuevo servidor:
- Host:
localhost - Port:
5432 - Database:
django_db - Username:
django_user - Password:
django_pass
- Host:
Opción 2: DBeaver (Universal)
- Descargar: https://dbeaver.io/download/
- New Connection → PostgreSQL
- Usa las mismas credenciales
Opción 3: VS Code
- Instala extensión: "PostgreSQL" de Chris Kolkman
- Conecta con las credenciales de arriba
Opción 4: Desde Docker (línea de comandos)
docker-compose exec db psql -U django_user -d django_dbComandos útiles en psql:
-- Ver todas las tablas
\dt
-- Describir tabla users
\d users_customuser
-- Ver usuarios
SELECT * FROM users_customuser;
-- Salir
\qSolución:
- Verifica que Docker Desktop esté corriendo
- En PowerShell, usa
docker compose(sin guión) si tienes Docker Desktop v2+ - Reinicia la terminal después de instalar Docker
Ver qué usa el puerto en Windows:
netstat -ano | findstr :8000Cambiar puerto en docker-compose.yml:
ports:
- "8001:8000" # Usa 8001 en lugar de 8000Soluciones:
- Espera 10-15 segundos después de
docker-compose up - Verifica que el contenedor de PostgreSQL esté healthy:
docker-compose ps- Revisa logs de la base de datos:
docker-compose logs dbCausa: Docker en Windows crea archivos con permisos diferentes
Soluciones:
- Ejecuta VS Code como administrador
- O crea los archivos manualmente en Windows y copia el contenido
- O cambia permisos:
icacls "users" /grant Everyone:F /TSolución:
- Docker Desktop → Settings → Resources → File Sharing
- Agrega la unidad donde está tu proyecto (C:, D:, etc.)
- Click "Apply & Restart"
Soluciones:
- Usa WSL 2 en lugar de Hyper-V:
- Docker Desktop → Settings → General
- Activa "Use WSL 2 based engine"
- Coloca el proyecto en WSL filesystem (mejor rendimiento):
- Abre WSL:
wsl - Crea proyecto en
/home/tuusuario/proyectos/
- Abre WSL:
- Aumenta recursos:
- Docker Desktop → Settings → Resources
- Asigna más CPU y RAM
Limpiar Docker:
# Ver espacio usado
docker system df
# Limpiar contenedores, imágenes y redes no usadas
docker system prune -a
# Limpiar volúmenes no usados (⚠️ cuidado)
docker volume pruneCausa: Sincronización de archivos entre Windows y Docker
Soluciones:
- Reinicia el contenedor:
docker-compose restart web- Si persiste, detén y vuelve a levantar:
docker-compose down
docker-compose up- Usa polling en Django (más lento pero funciona):
python manage.py runserver 0.0.0.0:8000 --noreloadSolución:
- Windows Security → Firewall & network protection
- Allow an app through firewall
- Busca "Docker Desktop" y permite redes privadas/públicas
Soluciones:
- Verifica que Docker Desktop esté corriendo (ícono de ballena)
- Si está "starting", espera a que termine
- Reinicia Docker Desktop:
- Click derecho → Restart
- Si persiste, reinicia Windows
# 1. Detener y eliminar todo
docker-compose down -v
# 2. Eliminar imágenes del proyecto
docker rmi proyecto-django-docker-web
# 3. Limpiar todo Docker (opcional)
docker system prune -a
# 4. Reconstruir sin caché
docker-compose build --no-cache
# 5. Levantar servicios
docker-compose up- Abre Docker Desktop
- Ve a "Containers" en el menú lateral
- Click en tu proyecto (
proyecto-django-docker) - Verás los logs de todos los contenedores con colores
- Puedes pausar, reiniciar o detener desde ahí
- Docker Desktop instalado y corriendo (ícono ballena activo)
- Terminal abierta (PowerShell, CMD o VS Code)
- Editor de texto (VS Code recomendado)
- Carpeta
proyecto-django-dockercreada -
Dockerfilecreado -
docker-compose.ymlcreado -
requirements.txtcreado -
.dockerignorecreado
-
docker-compose buildejecutado exitosamente - Proyecto Django creado con
django-admin startproject - App
userscreada constartapp -
settings.pyconfigurado (DATABASES, INSTALLED_APPS, AUTH_USER_MODEL) - Modelos creados en
users/models.py - Serializers creados en
users/serializers.py - Views creados en
users/views.py - URLs configuradas (
users/urls.pyymyproject/urls.py) - Admin registrado en
users/admin.py
-
makemigrationsejecutado sin errores -
migrateejecutado sin errores - Superusuario creado con
createsuperuser
-
docker-compose uplevantó ambos servicios -
docker-compose psmuestra contenedores "Up" - http://localhost:8000 responde (puede dar error 404, está bien)
- http://localhost:8000/admin/ muestra login
- http://localhost:8000/api/users/ muestra interfaz DRF
- Puedes crear usuario desde http://localhost:8000/api/users/
- Puedes hacer login desde http://localhost:8000/api/users/login/
- Puedes ver tu perfil en http://localhost:8000/api/users/me/
- Admin panel funciona con tu superusuario
-
docker-compose logsno muestra errores críticos - PostgreSQL healthcheck está "healthy"
- Puedes detener con
docker-compose down - Puedes volver a levantar con
docker-compose up
¡Proyecto completo funcionando en Windows! 🎉🪟
Una vez que domines este proyecto básico, puedes agregar:
- Agregar
docker-compose.prod.ymlpara producción - Usar variables
.enven lugar de hardcodear en docker-compose - Agregar Nginx como reverse proxy
- Implementar JWT tokens en lugar de sesiones
- Agregar Redis para caché
- Usar Gunicorn en lugar de runserver
- CI/CD con GitHub Actions
- Deploy en AWS/Azure/DigitalOcean
- Monitoreo con Prometheus + Grafana
- Logging centralizado con ELK Stack
- Implementar tests automatizados
- Multi-stage builds en Dockerfile
- Docker Desktop para Windows: https://docs.docker.com/desktop/windows/
- Docker Compose: https://docs.docker.com/compose/
- Django: https://docs.djangoproject.com/
- Docker para principiantes (español): https://www.youtube.com/results?search_query=docker+tutorial+español
- Django REST Framework: https://www.django-rest-framework.org/tutorial/quickstart/
- Postman: Para probar APIs (https://www.postman.com/)
- pgAdmin: Interfaz gráfica para PostgreSQL
- Docker Extension para VS Code: Gestión visual de contenedores
- WSL 2: Mejor rendimiento en Windows (https://docs.microsoft.com/en-us/windows/wsl/)
- Docker en español: https://discord.gg/docker (canal #spanish)
- Django Hispano: https://t.me/djangohispano
- Stack Overflow en español: https://es.stackoverflow.com/
- Usa WSL 2 para mejor rendimiento con Docker
- Coloca proyectos en WSL filesystem cuando uses WSL 2
- Reinicia Docker Desktop si algo no funciona
- Limpia Docker regularmente con
docker system prune - Lee los logs con
docker-compose logs -fpara debug - Usa Postman en lugar de curl en Windows (más fácil)
- No cierres Docker Desktop mientras los contenedores corren
- Asigna suficiente RAM en Docker Desktop Settings (min 4GB)
- Experimenta: Rompe cosas, arregla cosas, aprende
- Crea endpoints nuevos: Practica CRUD con otros modelos
- Agrega validaciones: Mejora los serializers
- Implementa permisos: Usuarios solo editan su perfil
- Agrega tests: Aprende TDD con Django
- Dockeriza otros proyectos: Aplica lo aprendido
¿Dudas? Revisa la sección de Troubleshooting o los logs con docker-compose logs -f
¡Éxito con Docker en Windows! 🐳🪟✨