Bucle de Verificación de Django
Ejecutar antes de PRs, después de cambios importantes y previo al despliegue para garantizar la calidad y seguridad de la aplicación Django.
Cuándo Activarlo
- Antes de abrir un pull request para un proyecto Django
- Después de cambios importantes en modelos, actualizaciones de migraciones o actualizaciones de dependencias
- Verificación previa al despliegue en staging o producción
- Ejecutar el pipeline completo: entorno → lint → prueba → seguridad → preparación para despliegue
- Validar la seguridad de las migraciones y la cobertura de pruebas
Fase 1: Comprobación del Entorno
# Verificar la versión de Python
python --version # Debe coincidir con los requisitos del proyecto
# Comprobar el entorno virtual
which python
pip list --outdated
# Verificar variables de entorno
python -c "import os; import environ; print('DJANGO_SECRET_KEY set' if os.environ.get('DJANGO_SECRET_KEY') else 'MISSING: DJANGO_SECRET_KEY')"
Si el entorno está mal configurado, detener y corregir.
Fase 2: Calidad y Formato del Código
# Comprobación de tipos
mypy . --config-file pyproject.toml
# Linting con ruff
ruff check . --fix
# Formateo con black
black . --check
black . # Corrección automática
# Ordenamiento de importaciones
isort . --check-only
isort . # Corrección automática
# Comprobaciones específicas de Django
python manage.py check --deploy
Problemas comunes:
- Falta de anotaciones de tipo en funciones públicas
- Violaciones de formato PEP 8
- Importaciones desordenadas
- Configuración de depuración dejada en configuración de producción
Fase 3: Migraciones
# Comprobar migraciones pendientes
python manage.py showmigrations
# Crear migraciones faltantes
python manage.py makemigrations --check
# Simulación de aplicación de migraciones
python manage.py migrate --plan
# Aplicar migraciones (entorno de prueba)
python manage.py migrate
# Comprobar conflictos de migración
python manage.py makemigrations --merge # Solo si existen conflictos
Informe:
- Número de migraciones pendientes
- Conflictos de migración
- Cambios en modelos sin migraciones
Fase 4: Pruebas + Cobertura
# Ejecutar todas las pruebas con pytest
pytest --cov=apps --cov-report=html --cov-report=term-missing --reuse-db
# Ejecutar pruebas de una aplicación específica
pytest apps/users/tests/
# Ejecutar con marcadores
pytest -m "not slow" # Omitir pruebas lentas
pytest -m integration # Solo pruebas de integración
# Informe de cobertura
open htmlcov/index.html
Informe:
- Total de pruebas: X pasadas, Y fallidas, Z omitidas
- Cobertura total: XX%
- Desglose de cobertura por aplicación
Objetivos de cobertura:
| Componente | Objetivo |
|---|---|
| Modelos | 90%+ |
| Serializers | 85%+ |
| Vistas | 80%+ |
| Servicios | 90%+ |
| Total | 80%+ |
Fase 5: Análisis de Seguridad
# Vulnerabilidades de dependencias
pip-audit
safety check --full-report
# Comprobaciones de seguridad de Django
python manage.py check --deploy
# Linter de seguridad Bandit
bandit -r . -f json -o bandit-report.json
# Escaneo de secretos (si gitleaks está instalado)
gitleaks detect --source . --verbose
# Comprobación de variable de entorno
python -c "from django.core.exceptions import ImproperlyConfigured; from django.conf import settings; settings.DEBUG"
Informe:
- Dependencias vulnerables encontradas
- Problemas de configuración de seguridad
- Secretos codificados detectados
- Estado del modo DEBUG (debe ser False en producción)
Fase 6: Comandos de Gestión de Django
# Comprobar problemas en modelos
python manage.py check
# Recopilar archivos estáticos
python manage.py collectstatic --noinput --clear
# Crear superusuario (si es necesario para pruebas)
echo "from apps.users.models import User; User.objects.create_superuser('[email protected]', 'admin')" | python manage.py shell
# Integridad de la base de datos
python manage.py check --database default
# Verificación de caché (si se usa Redis)
python -c "from django.core.cache import cache; cache.set('test', 'value', 10); print(cache.get('test'))"
Fase 7: Comprobaciones de Rendimiento
# Salida de Django Debug Toolbar (comprobar consultas N+1)
# Ejecutar en modo dev con DEBUG=True y acceder a una página
# Buscar consultas duplicadas en el panel SQL
# Análisis de conteo de consultas
django-admin debugsqlshell # Si django-debug-sqlshell está instalado
# Comprobar índices faltantes
python manage.py shell << EOF
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT table_name, index_name FROM information_schema.statistics WHERE table_schema = 'public'")
print(cursor.fetchall())
EOF
Informe:
- Número de consultas por página (debe ser < 50 para páginas típicas)
- Índices de base de datos faltantes
- Consultas duplicadas detectadas
Fase 8: Assets Estáticos
# Comprobar dependencias npm (si se usa npm)
npm audit
npm audit fix
# Construir archivos estáticos (si se usa webpack/vite)
npm run build
# Verificar archivos estáticos
ls -la staticfiles/
python manage.py findstatic css/style.css
Fase 9: Revisión de Configuración
# Ejecutar en shell de Python para verificar la configuración
python manage.py shell << EOF
from django.conf import settings
import os
# Comprobaciones críticas
checks = {
'DEBUG es False': not settings.DEBUG,
'SECRET_KEY configurada': bool(settings.SECRET_KEY and len(settings.SECRET_KEY) > 30),
'ALLOWED_HOSTS configurado': len(settings.ALLOWED_HOSTS) > 0,
'HTTPS habilitado': getattr(settings, 'SECURE_SSL_REDIRECT', False),
'HSTS habilitado': getattr(settings, 'SECURE_HSTS_SECONDS', 0) > 0,
'Base de datos configurada': settings.DATABASES['default']['ENGINE'] != 'django.db.backends.sqlite3',
}
for check, result in checks.items():
status = '✓' if result else '✗'
print(f"{status} {check}")
EOF
Fase 10: Configuración de Registro
# Probar salida de registro
python manage.py shell << EOF
import logging
logger = logging.getLogger('django')
logger.warning('Mensaje de advertencia de prueba')
logger.error('Mensaje de error de prueba')
EOF
# Comprobar archivos de registro (si están configurados)
tail -f /var/log/django/django.log
Fase 11: Documentación de API (si DRF)
# Generar esquema
python manage.py generateschema --format openapi-json > schema.json
# Validar esquema
# Comprobar si schema.json es JSON válido
python -c "import json; json.load(open('schema.json'))"
# Acceder a Swagger UI (si se usa drf-yasg)
# Visitar http://localhost:8000/swagger/ en el navegador
Fase 12: Revisión de Diferencias
# Mostrar estadísticas de diferencias
git diff --stat
# Mostrar cambios reales
git diff
# Mostrar archivos cambiados
git diff --name-only
# Comprobar problemas comunes
git diff | grep -i "todo\|fixme\|hack\|xxx"
git diff | grep "print(" # Sentencias de depuración
git diff | grep "DEBUG = True" # Modo de depuración
git diff | grep "import pdb" # Depurador
Lista de verificación:
- Sin sentencias de depuración (print, pdb, breakpoint())
- Sin comentarios TODO/FIXME en código crítico
- Sin secretos o credenciales codificados
- Migraciones de base de datos incluidas para cambios en modelos
- Cambios de configuración documentados
- Manejo de errores presente para llamadas externas
- Gestión de transacciones donde sea necesario
Plantilla de Salida
INFORME DE VERIFICACIÓN DE DJANGO
==================================
Fase 1: Comprobación del Entorno
✓ Python 3.11.5
✓ Entorno virtual activo
✓ Todas las variables de entorno configuradas
Fase 2: Calidad del Código
✓ mypy: Sin errores de tipo
✗ ruff: 3 problemas encontrados (corregidos automáticamente)
✓ black: Sin problemas de formato
✓ isort: Importaciones correctamente ordenadas
✓ manage.py check: Sin problemas
Fase 3: Migraciones
✓ Sin migraciones pendientes
✓ Sin conflictos de migración
✓ Todos los modelos tienen migraciones
Fase 4: Pruebas + Cobertura
Pruebas: 247 pasadas, 0 fallidas, 5 omitidas
Cobertura:
Total: 87%
users: 92%
products: 89%
orders: 85%
payments: 91%
Fase 5: Análisis de Seguridad
✗ pip-audit: 2 vulnerabilidades encontradas (requiere corrección)
✓ safety check: Sin problemas
✓ bandit: Sin problemas de seguridad
✓ Sin secretos detectados
✓ DEBUG = False
Fase 6: Comandos de Django
✓ collectstatic completado
✓ Integridad de la base de datos OK
✓ Backend de caché accesible
Fase 7: Rendimiento
✓ Sin consultas N+1 detectadas
✓ Índices de base de datos configurados
✓ Conteo de consultas aceptable
Fase 8: Assets Estáticos
✓ npm audit: Sin vulnerabilidades
✓ Assets construidos exitosamente
✓ Archivos estáticos recopilados
Fase 9: Configuración
✓ DEBUG = False
✓ SECRET_KEY configurada
✓ ALLOWED_HOSTS configurado
✓ HTTPS habilitado
✓ HSTS habilitado
✓ Base de datos configurada
Fase 10: Registro
✓ Registro configurado
✓ Archivos de registro modificables
Fase 11: Documentación de API
✓ Esquema generado
✓ Interfaz Swagger UI accesible
Fase 12: Revisión de Diferencias
Archivos cambiados: 12
+450, -120 líneas
✓ Sin sentencias de depuración
✓ Sin secretos codificados
✓ Migraciones incluidas
RECOMENDACIÓN: ADVERTENCIA: Corregir vulnerabilidades de pip-audit antes de desplegar
PRÓXIMOS PASOS:
1. Actualizar dependencias vulnerables
2. Re-ejecutar análisis de seguridad
3. Desplegar en staging para pruebas finales
Lista de Verificación Previa al Despliegue
- Todas las pruebas pasando
- Cobertura ≥ 80%
- Sin vulnerabilidades de seguridad
- Sin migraciones pendientes
- DEBUG = False en configuración de producción
- SECRET_KEY correctamente configurada
- ALLOWED_HOSTS configurado correctamente
- Copias de seguridad de base de datos habilitadas
- Archivos estáticos recopilados y servidos
- Registro configurado y funcionando
- Monitoreo de errores (Sentry, etc.) configurado
- CDN configurado (si aplica)
- Redis/backend de caché configurado
- Workers de Celery ejecutándose (si aplica)
- HTTPS/SSL configurado
- Variables de entorno documentadas
Integración Continua
Ejemplo de GitHub Actions
# .github/workflows/django-verification.yml
name: Verificación de Django
on: [push, pull_request]
jobs:
verify:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Configurar Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Cachear pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- name: Instalar dependencias
run: |
pip install -r requirements.txt
pip install ruff black mypy pytest pytest-django pytest-cov bandit safety pip-audit
- name: Comprobaciones de calidad de código
run: |
ruff check .
black . --check
isort . --check-only
mypy .
- name: Análisis de seguridad
run: |
bandit -r . -f json -o bandit-report.json
safety check --full-report
pip-audit
- name: Ejecutar pruebas
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
DJANGO_SECRET_KEY: test-secret-key
run: |
pytest --cov=apps --cov-report=xml --cov-report=term-missing
- name: Subir cobertura
uses: codecov/codecov-action@v3
Referencia Rápida
| Verificación | Comando |
|---|---|
| Entorno | python --version |
| Comprobación de tipos | mypy . |
| Linting | ruff check . |
| Formato | black . --check |
| Migraciones | python manage.py makemigrations --check |
| Pruebas | pytest --cov=apps |
| Seguridad | pip-audit && bandit -r . |
| Comprobación Django | python manage.py check --deploy |
| Recopilar estáticos | python manage.py collectstatic --noinput |
| Estadísticas de diff | git diff --stat |
Recuerda: La verificación automatizada detecta problemas comunes pero no reemplaza la revisión manual de código y las pruebas en el entorno de staging.