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