INSERT en Python
Insertar datos en MySQL desde Python es una operación fundamental que realizarás constantemente en tus aplicaciones. El conector mysql-connector-python facilita esta tarea mediante el uso de consultas parametrizadas que protegen contra inyecciones SQL, y ofrece métodos para insertar tanto registros individuales como múltiples filas de forma eficiente. En este artículo aprenderás las diferentes técnicas disponibles.
Requisitos previos
Necesitas una conexión configurada y la tabla productos creada. Si no la tienes, revisa el artículo de conexión para configurar el entorno.
USE tienda;
CREATE TABLE IF NOT EXISTS productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
categoria VARCHAR(50) NOT NULL,
precio DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0,
activo TINYINT(1) DEFAULT 1,
fecha_creacion DATETIME DEFAULT CURRENT_TIMESTAMP
);Código completo
Este ejemplo inserta un producto y muestra el ID generado:
import mysql.connector
def insertar_producto():
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
sql = 'INSERT INTO productos (nombre, categoria, precio, stock) VALUES (%s, %s, %s, %s)'
valores = ('Tablet Samsung Galaxy Tab S9', 'Tablets', 8999.00, 35)
cursor.execute(sql, valores)
conexion.commit()
print('Producto insertado exitosamente')
print(f'ID generado: {cursor.lastrowid}')
print(f'Filas afectadas: {cursor.rowcount}')
cursor.close()
conexion.close()
insertar_producto()Salida esperada:
Producto insertado exitosamente
ID generado: 9
Filas afectadas: 1
Explicación paso a paso
Cuando ejecutas una sentencia INSERT, los datos no se guardan en la base de datos hasta que llamas a conexion.commit(). Esto es parte del control de transacciones de MySQL. Si la conexión se cierra sin hacer commit, o si ocurre un error, los cambios se deshacen automáticamente (rollback implícito).
La propiedad cursor.lastrowid contiene el valor AUTO_INCREMENT generado para la última inserción, y cursor.rowcount indica cuántas filas se afectaron. Los placeholders %s se reemplazan por los valores de la tupla, y el conector se encarga de escapar y formatear cada valor según su tipo.
Si prefieres que los cambios se confirmen automáticamente, puedes configurar autocommit=True al crear la conexión:
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda',
autocommit=True
)Insertar desde un diccionario
Puedes usar la sintaxis de parámetros con nombre, lo que hace el código más legible cuando tienes muchos campos:
def crear_producto(producto):
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
sql = ('INSERT INTO productos (nombre, categoria, precio, stock) '
'VALUES (%(nombre)s, %(categoria)s, %(precio)s, %(stock)s)')
cursor.execute(sql, producto)
conexion.commit()
producto_id = cursor.lastrowid
cursor.close()
conexion.close()
return producto_id
# Uso
nuevo_id = crear_producto({
'nombre': 'Cargador USB-C 65W',
'categoria': 'Accesorios',
'precio': 599.00,
'stock': 300
})
print(f'Producto creado con ID: {nuevo_id}')Salida esperada:
Producto creado con ID: 10
Inserción masiva con executemany()
Cuando necesitas insertar múltiples registros, executemany() es mucho más eficiente que ejecutar INSERT en un bucle, porque envía los datos en lotes al servidor:
def insertar_multiples():
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda'
)
cursor = conexion.cursor()
sql = 'INSERT INTO productos (nombre, categoria, precio, stock) VALUES (%s, %s, %s, %s)'
productos = [
('Impresora HP LaserJet', 'Impresoras', 3499.00, 20),
('Cable HDMI 2.1 3m', 'Accesorios', 349.00, 500),
('Hub USB-C 7 en 1', 'Accesorios', 899.00, 150),
('Mousepad XXL Gaming', 'Accesorios', 449.00, 200),
('Soporte Monitor Ajustable', 'Accesorios', 1299.00, 75)
]
cursor.executemany(sql, productos)
conexion.commit()
print(f'Registros insertados: {cursor.rowcount}')
cursor.close()
conexion.close()
insertar_multiples()Salida esperada:
Registros insertados: 5
El método executemany() ejecuta la misma sentencia SQL múltiples veces con diferentes valores. Internamente, el conector optimiza la ejecución agrupando las inserciones cuando es posible.
Insertar con commit y rollback explícitos
Para operaciones que involucran múltiples inserciones relacionadas, es importante usar transacciones explícitas:
def registrar_pedido(cliente_id, items):
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda',
autocommit=False
)
try:
cursor = conexion.cursor()
# Insertar el pedido
cursor.execute(
'INSERT INTO pedidos (cliente_id, fecha, estado) VALUES (%s, NOW(), %s)',
(cliente_id, 'pendiente')
)
pedido_id = cursor.lastrowid
# Insertar líneas del pedido
for item in items:
cursor.execute(
'INSERT INTO detalle_pedido (pedido_id, producto_id, cantidad, precio_unitario) '
'VALUES (%s, %s, %s, %s)',
(pedido_id, item['producto_id'], item['cantidad'], item['precio_unitario'])
)
# Descontar stock
cursor.execute(
'UPDATE productos SET stock = stock - %s WHERE id = %s AND stock >= %s',
(item['cantidad'], item['producto_id'], item['cantidad'])
)
if cursor.rowcount == 0:
raise Exception(f"Stock insuficiente para producto {item['producto_id']}")
conexion.commit()
print(f'Pedido #{pedido_id} registrado con {len(items)} productos')
return pedido_id
except Exception as error:
conexion.rollback()
print(f'Error al registrar pedido: {error}')
return None
finally:
cursor.close()
conexion.close()Caso práctico
Veamos un ejemplo de importación de datos desde un archivo CSV a MySQL:
import mysql.connector
import csv
def importar_desde_csv(ruta_archivo):
conexion = mysql.connector.connect(
host='localhost',
user='root',
password='tu_contraseña',
database='tienda',
autocommit=False
)
cursor = conexion.cursor()
sql = 'INSERT INTO productos (nombre, categoria, precio, stock) VALUES (%s, %s, %s, %s)'
insertados = 0
errores = 0
try:
with open(ruta_archivo, 'r', encoding='utf-8') as archivo:
lector = csv.reader(archivo)
next(lector) # Saltar encabezados
lote = []
for fila in lector:
nombre, categoria, precio, stock = fila
lote.append((nombre, categoria, float(precio), int(stock)))
if len(lote) >= 100: # Insertar en lotes de 100
cursor.executemany(sql, lote)
insertados += cursor.rowcount
lote = []
if lote: # Insertar el último lote
cursor.executemany(sql, lote)
insertados += cursor.rowcount
conexion.commit()
print(f'Importación completada: {insertados} registros insertados')
except Exception as error:
conexion.rollback()
print(f'Error en la importación: {error}')
finally:
cursor.close()
conexion.close()Manejo de errores
Los errores más comunes al insertar datos son las violaciones de restricciones:
from mysql.connector import Error, errorcode
def insertar_seguro(conexion, nombre, categoria, precio, stock):
try:
cursor = conexion.cursor()
cursor.execute(
'INSERT INTO productos (nombre, categoria, precio, stock) VALUES (%s, %s, %s, %s)',
(nombre, categoria, precio, stock)
)
conexion.commit()
return {'exito': True, 'id': cursor.lastrowid}
except Error as error:
conexion.rollback()
if error.errno == errorcode.ER_DUP_ENTRY:
return {'exito': False, 'mensaje': 'Ya existe un producto con ese nombre'}
elif error.errno == errorcode.ER_BAD_NULL_ERROR:
return {'exito': False, 'mensaje': 'Faltan campos obligatorios'}
elif error.errno == errorcode.ER_DATA_TOO_LONG:
return {'exito': False, 'mensaje': 'Uno de los valores excede la longitud permitida'}
elif error.errno == errorcode.ER_NO_REFERENCED_ROW_2:
return {'exito': False, 'mensaje': 'Referencia a tabla externa no válida'}
else:
return {'exito': False, 'mensaje': f'Error: {error.msg}'}
finally:
cursor.close()Ahora que sabes cómo insertar datos, en el siguiente artículo aprenderás a actualizar registros existentes en MySQL desde Python.
Escrito por Eduardo Lázaro
