1. Home
  2. Aplicaciones y CMS
  3. Python
  4. Error decimal.InvalidOperation y fallo de login en Django

Error decimal.InvalidOperation y fallo de login en Django

🐍 Solución: Error decimal.InvalidOperation y fallo de login en Django con MariaDB 11.4

Si tu aplicación Django ha dejado de funcionar repentinamente tras una actualización del servidor a MariaDB 11.4, mostrando errores como decimal.InvalidOperation, fallas al iniciar sesión o un loop infinito en el login, tu base de datos está intacta.

⚠️ Aviso importante: Esta solución es de nivel avanzado y requiere acceso SSH al servidor y conocimientos de Python/Django. Si no te sientes cómodo realizando estos cambios por tu cuenta, abre un ticket de soporte y te asistimos directamente.

Este problema se debe a que MariaDB 11.4 cambió cómo retorna ciertos tipos de datos internos, lo que genera una cadena de incompatibilidades con las librerías Python que utiliza Django.

🔍 Síntomas: ¿Cómo saber si este es mi problema?

  • Error al cargar cualquier página: decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
  • Error al autenticar usuarios: AttributeError: 'str' object has no attribute 'utcoffset'
  • Loop infinito en el login: Al ingresar credenciales correctas, el sistema redirige de vuelta al login sin mostrar error.
  • El problema apareció de un día para otro, sin cambios en el código de la aplicación.

🧐 ¿Por qué ocurre esto?

MariaDB 11.4 cambió el type_code con el que retorna varios tipos de datos. Por ejemplo, campos que antes llegaban como enteros o datetime ahora llegan con type_code 0 (DECIMAL), y campos de texto largo llegan como bytes en lugar de str. Esto confunde a la librería mysqlclient de Python, que no puede procesarlos correctamente.

Los efectos en cadena son:

  • Django no puede conectarse a la base de datos → decimal.InvalidOperation
  • Los campos datetime llegan como strings → error utcoffset
  • Los datos de sesión llegan como bytes → Django no puede leer la sesión → loop en el login

1️⃣ Paso 1: Actualizar mysqlclient

La versión 2.2.4 de mysqlclient tiene bugs de compatibilidad con MariaDB 11.4. Debes actualizarla a 2.2.8 o superior. Ejecuta esto en la terminal dentro del virtualenv de tu proyecto:

source ~/virtualenv/TU_APP/3.12/bin/activate

MYSQLCLIENT_CFLAGS="-I/usr/include/mysql" \
MYSQLCLIENT_LDFLAGS="-L/usr/lib64 -lmariadb" \
PKG_CONFIG_PATH=/usr/lib64/pkgconfig \
pip install --upgrade mysqlclient --no-cache-dir

# Verificar versión instalada
python -c "import MySQLdb; print(MySQLdb.version_info)"
# Debe mostrar: (2, 2, 8, 'final', 0)

2️⃣ Paso 2: Actualizar Django

Django 5.1.x tiene un bug en el backend MySQL que impide la inicialización correcta con MariaDB 11.4. Actualiza a Django 5.2:

pip install "django==5.2" --no-cache-dir

# Verificar
python -c "import django; print(django.__version__)"
# Debe mostrar: 5.2

3️⃣ Paso 3: Agregar conversor de tipos en settings.py

Este es el cambio principal. Debes agregar un conversor de tipos personalizado al inicio de tu archivo settings.py, antes del bloque DATABASES, y luego referenciar ese conversor en las opciones de conexión.

Abre el archivo settings.py de tu proyecto y agrega el siguiente bloque justo antes de la sección DATABASES:

# ---- FIX MARIADB 11.4 (PremiumHosting) ----
import MySQLdb.converters
import datetime as _dt

def _smart_converter(val):
    """Convierte valores que MariaDB 11.4 retorna con type_code 0 (DECIMAL)
    pero que en realidad pueden ser enteros o datetimes."""
    if isinstance(val, (bytes, bytearray)):
        val = val.decode('ascii')
    if isinstance(val, str):
        # Intentar datetime primero
        try:
            return _dt.datetime.fromisoformat(val)
        except (ValueError, TypeError):
            pass
        # Intentar entero
        try:
            return int(float(val))
        except (ValueError, TypeError):
            pass
        return val
    if isinstance(val, _dt.datetime):
        return val
    try:
        return int(float(val))
    except (ValueError, TypeError):
        return val

def _blob_to_str(val):
    """Convierte campos BLOB/TEXT que llegan como bytes a string UTF-8."""
    if isinstance(val, (bytes, bytearray)):
        return val.decode('utf-8', errors='replace')
    return val

_conv = MySQLdb.converters.conversions.copy()
_conv[MySQLdb.constants.FIELD_TYPE.DECIMAL] = _smart_converter
_conv[MySQLdb.constants.FIELD_TYPE.NEWDECIMAL] = _smart_converter
_conv[0]   = _smart_converter   # type_code genérico que usa MariaDB 11.4
_conv[7]   = _smart_converter   # TIMESTAMP
_conv[12]  = _smart_converter   # DATETIME
_conv[255] = _blob_to_str       # BLOB
_conv[252] = _blob_to_str       # BLOB medium
_conv[251] = _blob_to_str       # BLOB long
_conv[250] = _blob_to_str       # BLOB tiny
# ---- FIN FIX MARIADB 11.4 ----

Luego, modifica el bloque DATABASES para usar el conversor y agregar el LOGIN_URL correcto. Tu configuración debe quedar similar a esta:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'tu_base_de_datos',
        'USER': 'tu_usuario',
        'PASSWORD': 'tu_password',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'conv': _conv,   # <-- Aquí se aplica el conversor
        },
    }
}

# Ajuste necesario con MariaDB 11.4 y USE_TZ
USE_TZ = False

# Asegúrate de que LOGIN_URL apunta a tu vista de login real
LOGIN_URL = '/tu-ruta-de-login/'

⚠️ Nota sobre USE_TZ: Cambiar USE_TZ = False deshabilita el soporte de zonas horarias en Django. Si tu aplicación maneja usuarios en múltiples zonas horarias, evalúa el impacto antes de aplicar este cambio.


4️⃣ Paso 4: Limpiar sesiones y caché

Las sesiones antiguas guardadas con Django 5.1 no pueden ser leídas por Django 5.2 debido a un cambio en el algoritmo de firma. Debes eliminarlas:

cd ~/tu_proyecto
source ~/virtualenv/TU_APP/3.12/bin/activate

python manage.py shell -c "
from django.contrib.sessions.models import Session
count = Session.objects.all().delete()
print('Sesiones eliminadas:', count)
"

# Eliminar caché de Python
find . -name "*.pyc" -delete
find . -name "__pycache__" -exec rm -rf {} + 2>/dev/null

5️⃣ Paso 5: Reiniciar la aplicación

Si tu aplicación usa Passenger (lo más común en cPanel con Python), reiníciala tocando el archivo restart.txt:

touch ~/tu_proyecto/tmp/restart.txt

Si usas otro servidor WSGI, reinicia el proceso correspondiente.


✅ Verificación final

Puedes verificar que todo funciona correctamente ejecutando este comando en el virtualenv de tu proyecto:

python manage.py shell -c "
from django.db import connection
cursor = connection.cursor()
cursor.execute('SELECT VERSION()')
print('Conexión OK:', cursor.fetchone())

from django.contrib.auth import authenticate
user = authenticate(username='tu_usuario', password='tu_password')
print('Login OK:', user)
"

Si ambas líneas retornan valores correctos, tu aplicación está lista.


📋 Resumen de cambios

  • mysqlclient: 2.2.4 → 2.2.8
  • Django: 5.1.x → 5.2
  • settings.py: Conversor de tipos personalizado para compatibilidad con MariaDB 11.4
  • settings.py: USE_TZ = False para evitar errores de timezone con datetimes
  • settings.py: LOGIN_URL apuntando a la ruta correcta de login de la aplicación
  • Base de datos: Sesiones antiguas eliminadas por incompatibilidad entre versiones de Django

⚠️ ¿Necesitas ayuda? Si los pasos anteriores te parecen complejos o tu aplicación tiene una configuración diferente (múltiples bases de datos, timezone awareness, Gunicorn, uWSGI, etc.), abre un ticket de soporte con el nombre de tu dominio y te asistimos directamente en tu entorno. Cada aplicación Django puede tener particularidades que requieren ajustes adicionales.

Updated on 10 de abril de 2026

Was this article helpful?

Leave a Comment