Python Tutorial
Connecting Python to MySQL
MySQL is one of the most widely used database servers for web applications. Python connects to it with a driver package — the official mysql-connector-python from Oracle, or the pure-Python PyMySQL. Both follow the DB-API you learned with sqlite3, so the code looks almost the same; the main differences are connection settings, the %s placeholder style, and running a separate server.
These examples need a running MySQL server and the driver installed, so run them on your own computer.
Setup
- Install MySQL Server (or run it with Docker:
docker run -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 mysql:8.4). - Create a database and a dedicated user:
CREATE DATABASE shop; CREATE USER 'shop_app'@'%' IDENTIFIED BY '...'; GRANT ALL ON shop.* TO 'shop_app'@'%'; - Install the driver into your virtual environment:
pip install mysql-connector-python(orpip install pymysql). - Keep the password out of your code — read it from an environment variable or a secrets manager.
Differences from sqlite3
Placeholders are %s (or %(name)s) for every type — never use Python's % operator yourself. Autocommit is off by default, so call conn.commit(). conn.cursor(dictionary=True) returns rows as dictionaries. AUTO_INCREMENT ids are available from cursor.lastrowid. For web applications, use a connection pool so requests reuse connections instead of opening a new one each time.
Examples
Connecting and handling connection errors
# pip install mysql-connector-python
import os
import mysql.connector
from mysql.connector import errorcode
config = {
"host": "localhost",
"port": 3306,
"user": "shop_app",
"password": os.environ["DB_PASSWORD"], # never hard-code passwords
"database": "shop",
}
try:
conn = mysql.connector.connect(**config)
except mysql.connector.Error as err:
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Wrong user name or password")
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exist")
else:
print(err)
else:
print("connected:", conn.is_connected(), "| server version:", conn.get_server_info())
conn.close()
connected: True | server version: 8.4.2
Create a table and perform CRUD with %s placeholders
import os
import mysql.connector
conn = mysql.connector.connect(host="localhost", user="shop_app",
password=os.environ["DB_PASSWORD"], database="shop")
cur = conn.cursor(dictionary=True) # rows as dicts
cur.execute("""
CREATE TABLE IF NOT EXISTS customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
city VARCHAR(50)
)
""")
cur.execute("INSERT INTO customers (name, email, city) VALUES (%s, %s, %s)",
("Asha", "asha@example.com", "Pune"))
print("new id:", cur.lastrowid)
cur.executemany("INSERT INTO customers (name, email, city) VALUES (%s, %s, %s)", [
("Ravi", "ravi@example.com", "Mumbai"),
("Meera", "meera@example.com", "Pune"),
])
conn.commit() # autocommit is off by default
cur.execute("SELECT id, name, city FROM customers WHERE city = %s ORDER BY name", ("Pune",))
for row in cur.fetchall():
print(row)
cur.execute("UPDATE customers SET city = %s WHERE email = %s", ("Nagpur", "ravi@example.com"))
print("updated:", cur.rowcount)
cur.execute("DELETE FROM customers WHERE email = %s", ("meera@example.com",))
print("deleted:", cur.rowcount)
conn.commit()
cur.close()
conn.close()
new id: 1
{'id': 1, 'name': 'Asha', 'city': 'Pune'}
{'id': 3, 'name': 'Meera', 'city': 'Pune'}
updated: 1
deleted: 1
Transactions with a connection pool
import os
import mysql.connector
from mysql.connector import pooling
pool = pooling.MySQLConnectionPool(
pool_name="shop_pool",
pool_size=5,
host="localhost",
user="shop_app",
password=os.environ["DB_PASSWORD"],
database="shop",
)
def place_order(customer_id, items):
conn = pool.get_connection()
try:
conn.start_transaction()
cur = conn.cursor()
cur.execute("INSERT INTO orders (customer_id) VALUES (%s)", (customer_id,))
order_id = cur.lastrowid
cur.executemany(
"INSERT INTO order_items (order_id, product, quantity) VALUES (%s, %s, %s)",
[(order_id, product, qty) for product, qty in items],
)
conn.commit() # the order and all its items, or nothing
return order_id
except mysql.connector.Error:
conn.rollback()
raise
finally:
conn.close() # returns the connection to the pool
print("order", place_order(1, [("Pen", 3), ("Notebook", 2)]), "placed")
order 101 placed
The same queries with PyMySQL
# pip install pymysql
import os
import pymysql
conn = pymysql.connect(host="localhost", user="shop_app", password=os.environ["DB_PASSWORD"],
database="shop", cursorclass=pymysql.cursors.DictCursor)
with conn:
with conn.cursor() as cur:
cur.execute("SELECT name, city FROM customers WHERE id = %s", (1,))
print(cur.fetchone())
{'name': 'Asha', 'city': 'Pune'}
Common Mistakes
- Using ? placeholders (sqlite3 style) with MySQL drivers, which expect %s.
- Formatting values into the query with % or f-strings instead of passing them as the second argument.
- Forgetting conn.commit() because autocommit is off.
- Hard-coding database passwords in source code or committing them to Git.
- Opening a new connection for every request in a web app instead of using a pool.
Key Points to Remember
- mysql-connector-python and PyMySQL are DB-API drivers for MySQL.
- Use %s / %(name)s placeholders and pass values separately.
- Commit explicitly; roll back on errors.
- cursor(dictionary=True) or DictCursor returns rows as dictionaries.
- Use connection pooling and environment variables for credentials in real applications.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.