← Volver a Guía Principal

🐛 ERRORES COMUNES Y SOLUCIONES

VERSIÓN 5.0 - EXPERIENCIA REAL

Errores encontrados durante desarrollo y sus soluciones comprobadas

Universidad Tecnológica de Hermosillo | Profesor: Bernardo Prado

⚠️ SOBRE ESTA GUÍA

Esta guía documenta errores REALES encontrados por estudiantes al seguir la guía principal paso a paso.

Beneficio: Si encuentras estos errores, aquí está la solución comprobada que ya funcionó.

  • 4 errores documentados con soluciones detalladas
  • Capturas de pantalla de los errores exactos
  • Explicación técnica del por qué ocurren
  • Código corregido listo para copiar y pegar

📋 ÍNDICE DE ERRORES

Error Descripción Sección
1 ValueError: Invalid model reference 'oauth2_provider.Models.AccessToken' 3.2 → Configuración OAuth Provider
2 Unknown command: 'show_urls' 3.3.1 → Configurar URLs OAuth
3 Redirect URI mismatch / Loop infinito de login 3.4 → oauth_views.py
4 Token OAuth retorna 401 en test_oauth.py 2.2 → Configurar JWT + OAuth

1ERROR EN OAUTH2_PROVIDER SETTINGS

📍 UBICACIÓN EN LA GUÍA

Sección 3.2: Instalar y configurar Django Allauth

Subsección 2.5: Agregar Configuración de OAuth 2.0 Provider

ERROR ValueError en settings.py

ValueError: Invalid model reference 'oauth2_provider.Models.AccessToken'. 
String model references must be of the form 'app_label.ModelName'.

Ocurre al ejecutar: python manage.py check

🔍 ¿POR QUÉ OCURRE?

Django espera que las referencias a modelos sigan el formato 'app_label.ModelName'

El código en la guía incluye erróneamente .Models. en la ruta, causando que Django no reconozca el formato.

Formato incorrecto: 'oauth2_provider.Models.AccessToken'

Formato correcto: 'oauth2_provider.AccessToken'

✅ SOLUCIÓN

1Ubicar el archivo

Abre biblioteca_project/settings.py

2Buscar la sección OAUTH2_PROVIDER

Busca estas líneas (aproximadamente línea 200-220):

# ❌ CÓDIGO INCORRECTO (de la guía original)
OAUTH2_PROVIDER = {
    'SCOPES': {
        'read': 'Read scope',
        'write': 'Write scope',
    },
    'ACCESS_TOKEN_EXPIRE_SECONDS': 36000,
    'REFRESH_TOKEN_EXPIRE_SECONDS': 86400,
    'ACCESS_TOKEN_MODEL': 'oauth2_provider.Models.AccessToken',  # ❌ ERROR
    'REFRESH_TOKEN_MODEL': 'oauth2_provider.Models.RefreshToken',  # ❌ ERROR
}

3Corregir eliminando ".Models"

Cambia las dos últimas líneas a:

# ✅ CÓDIGO CORRECTO
OAUTH2_PROVIDER = {
    'SCOPES': {
        'read': 'Read scope',
        'write': 'Write scope',
    },
    'ACCESS_TOKEN_EXPIRE_SECONDS': 36000,
    'REFRESH_TOKEN_EXPIRE_SECONDS': 86400,
    'ACCESS_TOKEN_MODEL': 'oauth2_provider.AccessToken',  # ✅ CORRECTO
    'REFRESH_TOKEN_MODEL': 'oauth2_provider.RefreshToken',  # ✅ CORRECTO
}

4Verificar que funciona

python manage.py check

✅ Salida esperada

System check identified no issues (0 silenced).

💡 EXPLICACIÓN TÉCNICA

¿Qué significan estos modelos?

  • ACCESS_TOKEN_MODEL: Define qué modelo usar para almacenar tokens de acceso OAuth
  • REFRESH_TOKEN_MODEL: Define qué modelo usar para almacenar tokens de refresco

Formato de referencia Django:

'nombre_app.NombreModelo'

NO incluir subcarpetas o módulos como .Models.

2COMANDO show_urls NO ENCONTRADO

📍 UBICACIÓN EN LA GUÍA

Sección 3.3.1: Configurar URLs de OAuth 2.0

Al ejecutar: python manage.py show_urls

ERROR Unknown command

Unknown command: 'show_urls'
Type 'manage.py help' for usage.

🔍 ¿POR QUÉ OCURRE?

El comando show_urls NO es nativo de Django.

Pertenece al paquete django-extensions que NO está instalado por defecto.

La guía asume que esta librería ya está instalada, pero hay que hacerlo manualmente.

✅ SOLUCIÓN

1Instalar django-extensions

# En tu terminal con el entorno virtual activado:
pip install django-extensions

2Agregar a INSTALLED_APPS

Abre biblioteca_project/settings.py y agrega:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third party apps
    'rest_framework',
    'rest_framework_simplejwt',
    'corsheaders',
    'oauth2_provider',
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    'django_extensions',  # ⬅️ AGREGAR ESTA LÍNEA
    
    # Local apps
    'libros',
]

⚠️ IMPORTANTE: Guion bajo

Nota que en pip instalamos django-extensions (con guion)

Pero en INSTALLED_APPS ponemos 'django_extensions' (con guion bajo)

3Probar el comando

python manage.py show_urls

✅ Salida esperada (parcial)

/admin/                                admin:index
/api/libros/                          libros-list
/api/auth/login/                      token_obtain_pair
/api/auth/google/redirect/            google_oauth_redirect
/api/auth/google/callback/            google_oauth_callback
/o/authorize/                         oauth2_provider:authorize
/o/token/                             oauth2_provider:token
...
(mostrará todas tus rutas)

📦 ¿Qué es django-extensions?

django-extensions es un paquete que agrega comandos útiles para desarrollo:

  • show_urls - Muestra todas las rutas de tu proyecto
  • shell_plus - Shell mejorado con imports automáticos
  • graph_models - Genera diagramas de tus modelos
  • runserver_plus - Servidor de desarrollo mejorado
  • Y muchos más...

Recomendación: Úsalo solo en desarrollo, no en producción.

💾 Actualizar requirements.txt

Para que no olvides instalarlo en el futuro:

# Agregar a requirements.txt:
django-extensions==3.2.3

3PROBLEMAS CON REDIRECT_URI EN OAUTH

📍 UBICACIÓN EN LA GUÍA

Sección 3.4: Crear Vista de OAuth y Generar JWT

Archivo: libros/oauth_views.py

ERROR 1 redirect_uri_mismatch

Al intentar loguearse con Google, aparece error de redirección.

🖼️ [Captura: Ventana de error de Google OAuth]

"Error 400: redirect_uri_mismatch"

ERROR 2 Loop infinito de login

Después de autenticarse, regresa a la pantalla de Django REST una y otra vez.

No muestra la información del usuario logueado.

🖼️ [Captura: Terminal mostrando GET en verde pero sin procesar login]

🔍 ¿POR QUÉ OCURRE?

CAUSA DEL ERROR 1: Inconsistencia en redirect_uri

Las dos funciones en oauth_views.py usan URLs diferentes:

  • google_oauth_callback usa: http://localhost:8000/...
  • google_oauth_redirect usa: http://127.0.0.1:8000/...

Problema: Google OAuth requiere que la URL de callback sea EXACTAMENTE la misma que se registró y la que se envía en la solicitud.

CAUSA DEL ERROR 2: Flujo OAuth incompleto

Posibles causas:

  • El token no se está guardando correctamente
  • El frontend no está recibiendo el token
  • Falta el procesamiento en oauth_login.html

✅ SOLUCIÓN PARA ERROR 1: Consistencia en URLs

1Decidir qué URL usar

Recomendación: Usa 127.0.0.1 en lugar de localhost

Razón: Es más confiable y evita problemas de resolución DNS en algunos sistemas.

2Abrir oauth_views.py

Ubicación: libros/oauth_views.py

3Corregir AMBAS funciones

Función 1: google_oauth_callback

@api_view(['GET', 'POST'])
@permission_classes([AllowAny])
def google_oauth_callback(request):
    """Callback de Google OAuth"""
    code = request.GET.get('code')
    
    if not code:
        return Response({'error': 'No code provided'}, status=400)
    
    token_url = 'https://oauth2.googleapis.com/token'
    token_data = {
        'code': code,
        'client_id': settings.SOCIALACCOUNT_PROVIDERS['google']['APP']['client_id'],
        'client_secret': settings.SOCIALACCOUNT_PROVIDERS['google']['APP']['secret'],
        'redirect_uri': 'http://127.0.0.1:8000/api/auth/google/callback/',  # ✅ USAR 127.0.0.1
        'grant_type': 'authorization_code',
    }
    
    # ... resto del código

Función 2: google_oauth_redirect

@api_view(['GET'])
@permission_classes([AllowAny])
def google_oauth_redirect(request):
    """Genera URL de autorización de Google"""
    google_auth_url = 'https://accounts.google.com/o/oauth2/v2/auth'
    
    params = {
        'client_id': settings.SOCIALACCOUNT_PROVIDERS['google']['APP']['client_id'],
        'redirect_uri': 'http://127.0.0.1:8000/api/auth/google/callback/',  # ✅ USAR 127.0.0.1
        'response_type': 'code',
        'scope': 'openid email profile',
        'access_type': 'offline',
    }
    
    # ... resto del código

4Verificar en Google Cloud Console

Asegúrate de que en Google Cloud Console tengas AMBAS URIs:

http://127.0.0.1:8000/api/auth/google/callback/
http://localhost:8000/api/auth/google/callback/

Tener ambas asegura compatibilidad en cualquier caso

✅ SOLUCIÓN PARA ERROR 2: Loop infinito

⚠️ PROBLEMA COMPLEJO

Este error requiere revisión detallada del flujo OAuth completo.

Según el reporte: "Probé con la primera versión que tenía la guía, más los cambios que le había hecho, y en esa sí me deja logguearme"

Conclusión: Hay una discrepancia entre versiones de oauth_views.py

🔧 Pasos de diagnóstico:

1Verificar que el token se está generando

Agrega prints de debug en google_oauth_callback:

def google_oauth_callback(request):
    code = request.GET.get('code')
    print(f"📥 Code recibido: {code}")  # Debug
    
    # ... solicitar token ...
    
    print(f"✅ Access token: {google_token_response.get('access_token')}")  # Debug
    print(f"✅ JWT generado: {jwt_token}")  # Debug
    
    # ... resto del código

2Verificar el script de oauth_login.html

Usa la versión corregida del Error #3 de la Guía v3.0

3Revisar el return en la función callback

Asegúrate de que la función retorna correctamente:

# Al final de google_oauth_callback:
return redirect(
    f'/oauth/login/?access={jwt_token}&refresh={refresh_token}'
    f'&email={user_info.get("email")}&username={user.username}'
)

📧 ARCHIVO ADJUNTO MENCIONADO

El estudiante menciona: "Ese archivo se lo adjuntaré en el correo"

Acción: Si tienes acceso a ese archivo oauth_views.py que funciona, úsalo como referencia.

Alternativa: Usa la versión de la Guía v3.0 que ya tiene correcciones aplicadas.

4TOKEN OAUTH RETORNA 401 EN TESTS

📍 UBICACIÓN EN LA GUÍA

Sección 2.2: Configurar JWT en Settings

Al ejecutar: python test_oauth.py

ERROR Token OAuth no es válido

=== Obteniendo Token OAuth 2.0 ===
✅ Token obtenido: gCMkTVM8G1fLnegrmhkqTzdrnNbCk3...

Status Code: 401
Data: {
    'detail': 'El token dado no es valido para ningun tipo de token',
    'code': 'token_not_valid',
    'messages': [{
        'token_class': 'AccessToken',
        'token_type': 'access',
        'message': 'Token is invalid'
    }]
}

🔍 ¿POR QUÉ OCURRE?

CAUSA: Orden de autenticación en REST_FRAMEWORK

Django REST Framework procesa los métodos de autenticación EN ORDEN

Si JWTAuthentication está PRIMERO, intentará validar el token OAuth como si fuera JWT, fallando con error 401.

Orden INCORRECTO:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # ⬅️ Primero
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',  # ⬅️ Segundo
    ],
}

Problema: Cuando llega un token OAuth, JWTAuthentication lo intenta validar primero y falla.

✅ SOLUCIÓN: Cambiar el orden

1Abrir settings.py

Ubicación: biblioteca_project/settings.py

2Buscar REST_FRAMEWORK

Busca la configuración de REST_FRAMEWORK (aproximadamente línea 150-170)

3Cambiar el orden

Pon OAuth2Authentication PRIMERO:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',  # ✅ PRIMERO
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # Segundo
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

4Probar test_oauth.py nuevamente

python test_oauth.py

✅ Salida esperada

=== Obteniendo Token OAuth 2.0 ===
✅ Token obtenido: gCMkTVM8G1fLnegrmhkqTzdrnNbCk3...

Status Code: 200
Data: {
    'count': 10,
    'next': 'http://127.0.0.1:8000/api/libros/?page=2',
    'previous': None,
    'results': [
        {
            'id': 1,
            'titulo': 'El Quijote',
            'autor': 'Miguel de Cervantes',
            ...
        },
        ...
    ]
}

📚 EXPLICACIÓN TÉCNICA

¿Cómo funciona DEFAULT_AUTHENTICATION_CLASSES?

Django REST Framework intenta autenticar en el orden especificado:

  1. Intenta el primer método
    • Si funciona: ✅ Usuario autenticado
    • Si falla: Pasa al siguiente
  2. Intenta el segundo método
    • Si funciona: ✅ Usuario autenticado
    • Si falla: Pasa al siguiente
  3. Si todos fallan: Retorna 401 Unauthorized

¿Por qué OAuth2Authentication primero?

  • Tokens OAuth tienen formato específico más fácil de identificar
  • JWTAuthentication puede confundirse con otros tipos de token
  • OAuth2Authentication rechaza rápido si no es token OAuth

💡 ¿AFECTA A JWT?

NO. Cambiar el orden NO afecta el funcionamiento de JWT.

Si llega un token JWT válido:

  1. OAuth2Authentication lo rechaza (no es token OAuth)
  2. Pasa a JWTAuthentication
  3. JWTAuthentication lo valida ✅

Ambos tipos de token funcionan perfectamente.

✅ CHECKLIST DE VERIFICACIÓN

Antes de continuar, verifica que corregiste:

# Corrección Aplicada
1 Eliminado .Models. en OAUTH2_PROVIDER settings
2 Instalado django-extensions y agregado a INSTALLED_APPS
3 Unificado redirect_uri en ambas funciones de oauth_views.py
4 OAuth2Authentication ANTES de JWTAuthentication en REST_FRAMEWORK
python manage.py check sin errores
python manage.py show_urls funciona
Login con Google funciona correctamente
python test_oauth.py retorna 200 OK

📚 RECURSOS Y AYUDA ADICIONAL

💡 Consejos para evitar errores:

  • Lee con atención: Cada carácter cuenta en programación
  • Copia y pega con cuidado: Verifica que no haya caracteres extra
  • Prueba frecuentemente: python manage.py check después de cada cambio
  • Usa Git: Haz commits frecuentes para poder revertir cambios
  • Lee los mensajes de error: Django es muy descriptivo en sus errores

📧 ¿Encontraste otro error?

Si encuentras un error que NO está en esta guía:

  1. Documenta el error completo (screenshot + mensaje)
  2. Anota qué estabas haciendo cuando ocurrió
  3. Comparte con tus compañeros
  4. Ayudarás a futuros estudiantes 🎓