UPDATE en Python
Actualizar datos existentes en MySQL desde Python es una operación que realizarás frecuentemente en cualquier aplicación. El conector mysql-connector-python permite ejecutar sentencias UPDATE parametrizadas de forma segura, verificar cuántas filas se modificaron y controlar las transacciones de manera explícita. En este artículo aprenderás las diferentes técnicas para actualizar registros de forma efectiva.
Requisitos previos
Necesitas una conexión configurada y la tabla productos con datos de prueba. Si seguiste los artículos anteriores, ya deberías tener todo preparado.
Código completo
Este ejemplo actualiza el precio y stock de un producto específico:
import mysql.connector
def actualizar_producto():
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
cursor.execute(
'UPDATE productos SET precio = %s, stock = %s WHERE id = %s',
(13499.99, 30, 1)
)
conexion.commit()
print(f'Filas afectadas: {cursor.rowcount}')
cursor.close()
conexion.close()
actualizar_producto()Salida esperada:
Filas afectadas: 1
Explicación paso a paso
El resultado de una sentencia UPDATE se refleja en la propiedad cursor.rowcount, que indica cuántas filas fueron afectadas por la operación. Si el valor es 0, significa que ninguna fila cumplió la condición WHERE, lo que normalmente indica que el registro no existe o ya fue eliminado.
A diferencia de Node.js, el conector de Python no distingue entre filas encontradas y filas realmente cambiadas en rowcount. Sin embargo, puedes activar la opción client_flags con CLIENT.FOUND_ROWS para que rowcount devuelva el número de filas encontradas en lugar de las cambiadas:
from mysql.connector.constants import ClientFlag
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda',
client_flags=[ClientFlag.FOUND_ROWS]
)Verificar si la actualización tuvo efecto
def actualizar_precio(producto_id, nuevo_precio):
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
cursor.execute(
'UPDATE productos SET precio = %s WHERE id = %s',
(nuevo_precio, producto_id)
)
conexion.commit()
if cursor.rowcount == 0:
print(f'No se encontró el producto con ID {producto_id}')
resultado = False
else:
print(f'Precio del producto {producto_id} actualizado a ${nuevo_precio}')
resultado = True
cursor.close()
conexion.close()
return resultado
actualizar_precio(1, 12499.99)
actualizar_precio(999, 100.00)Salida esperada:
Precio del producto 1 actualizado a $12499.99
No se encontró el producto con ID 999
Actualización dinámica
Cuando el usuario puede modificar solo algunos campos, necesitas construir la consulta dinámicamente:
def actualizar_campos(producto_id, **campos):
columnas_permitidas = {'nombre', 'categoria', 'precio', 'stock', 'activo'}
campos_validos = {k: v for k, v in campos.items() if k in columnas_permitidas}
if not campos_validos:
print('No se proporcionaron campos válidos para actualizar')
return False
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
set_clause = ', '.join(f'{campo} = %s' for campo in campos_validos)
valores = list(campos_validos.values()) + [producto_id]
cursor = conexion.cursor()
cursor.execute(
f'UPDATE productos SET {set_clause} WHERE id = %s',
tuple(valores)
)
conexion.commit()
modificado = cursor.rowcount > 0
print(f'Campos actualizados: {list(campos_validos.keys())}' if modificado
else 'Producto no encontrado')
cursor.close()
conexion.close()
return modificado
# Actualizar solo precio y stock
actualizar_campos(1, precio=11999.99, stock=50)
# Intentar actualizar con campo no permitido
actualizar_campos(1, precio=10000, campo_malicioso='valor')Salida esperada:
Campos actualizados: ['precio', 'stock']
Campos actualizados: ['precio']
Actualización con parámetros nombrados
El conector permite usar parámetros con nombre para mayor claridad:
def actualizar_con_nombres(producto_id, datos):
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
cursor.execute(
'UPDATE productos SET nombre = %(nombre)s, precio = %(precio)s '
'WHERE id = %(id)s',
{'nombre': datos['nombre'], 'precio': datos['precio'], 'id': producto_id}
)
conexion.commit()
print(f'Filas actualizadas: {cursor.rowcount}')
cursor.close()
conexion.close()
actualizar_con_nombres(2, {'nombre': 'Mouse Logitech MX Master 3S', 'precio': 1799.00})Caso práctico
Veamos un caso real: una función para actualizar inventario después de recibir mercancía, con verificación y registro de auditoría:
import mysql.connector
from datetime import datetime
def recibir_mercancia(movimientos):
"""
Actualiza el stock de múltiples productos al recibir mercancía.
movimientos: lista de diccionarios {'producto_id': int, 'cantidad': int}
"""
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda',
autocommit=False
)
try:
cursor = conexion.cursor(dictionary=True)
actualizados = 0
no_encontrados = []
for mov in movimientos:
# Verificar que el producto existe
cursor.execute(
'SELECT id, nombre, stock FROM productos WHERE id = %s',
(mov['producto_id'],)
)
producto = cursor.fetchone()
if not producto:
no_encontrados.append(mov['producto_id'])
continue
stock_anterior = producto['stock']
nuevo_stock = stock_anterior + mov['cantidad']
# Actualizar stock
cursor.execute(
'UPDATE productos SET stock = %s WHERE id = %s',
(nuevo_stock, mov['producto_id'])
)
# Registrar en historial
cursor.execute(
'INSERT INTO historial_inventario '
'(producto_id, tipo, cantidad, stock_anterior, stock_nuevo, fecha) '
'VALUES (%s, %s, %s, %s, %s, NOW())',
(mov['producto_id'], 'entrada', mov['cantidad'],
stock_anterior, nuevo_stock)
)
print(f" {producto['nombre']}: {stock_anterior} -> {nuevo_stock} (+{mov['cantidad']})")
actualizados += 1
conexion.commit()
print(f'\nResumen: {actualizados} productos actualizados')
if no_encontrados:
print(f'No encontrados: {no_encontrados}')
except Exception as error:
conexion.rollback()
print(f'Error: {error}')
finally:
cursor.close()
conexion.close()
# Uso
recibir_mercancia([
{'producto_id': 1, 'cantidad': 10},
{'producto_id': 2, 'cantidad': 50},
{'producto_id': 3, 'cantidad': 25}
])Salida esperada:
Laptop HP Pavilion: 25 -> 35 (+10)
Mouse Logitech MX Master: 150 -> 200 (+50)
Teclado Mecánico Corsair K70: 80 -> 105 (+25)
Resumen: 3 productos actualizados
Manejo de errores
Las actualizaciones pueden fallar por violaciones de restricciones o problemas de concurrencia:
from mysql.connector import Error, errorcode
def actualizar_seguro(conexion, producto_id, datos):
try:
cursor = conexion.cursor()
cursor.execute(
'UPDATE productos SET nombre = %s, precio = %s WHERE id = %s',
(datos.get('nombre'), datos.get('precio'), producto_id)
)
conexion.commit()
if cursor.rowcount == 0:
return {'error': 'NOT_FOUND', 'mensaje': 'Producto no encontrado'}
return {'exito': True}
except Error as error:
conexion.rollback()
if error.errno == errorcode.ER_DUP_ENTRY:
return {'error': 'DUPLICADO', 'mensaje': 'Ya existe un producto con ese nombre'}
elif error.errno == errorcode.ER_DATA_TOO_LONG:
return {'error': 'LONGITUD', 'mensaje': 'El nombre excede el límite de caracteres'}
elif error.errno == errorcode.ER_LOCK_WAIT_TIMEOUT:
return {'error': 'TIMEOUT', 'mensaje': 'El registro está siendo modificado por otro proceso'}
else:
return {'error': 'INTERNO', 'mensaje': f'Error: {error.msg}'}
finally:
cursor.close()Ahora que conoces las técnicas para actualizar registros, en el siguiente artículo aprenderás a eliminar datos de MySQL desde Python.
Escrito por Eduardo Lázaro
