Connecting Python to MySQL
Quick answer MySQL is a server and your Python program is just another client, so every connectivity program is the same chain — import mysql.connector, connect(), cursor(), execute() — and each link breaks with its own recognisable error.
MySQL is a server. It is a program that keeps running in the background and listens on a port — 3306 unless someone changed it. Your data lives inside that server, not inside your .py file. So Python never opens the database files directly. It becomes a client and asks the server, exactly the way the mysql command-line tool does.
The library that speaks MySQL's protocol on Python's behalf is called a connector. The one CBSE names is mysql-connector-python. Install it once from the command prompt:
pip install mysql-connector-pythonNote the trap straight away: the package you install is mysql-connector-python, but the name you import is mysql.connector. Two different strings for the same thing.
Every connectivity program, however large, is these five steps:
- Import the connector.
- connect() — open a line to the server. It returns a connection object.
- cursor() — ask the connection for a cursor object. The cursor is what actually carries SQL to the server and holds whatever comes back.
- execute() — send one SQL statement.
- Fetch the rows (for
SELECT) or commit() (forINSERT/UPDATE/DELETE), then close.
Why two objects instead of one? Because the connection is the line and the cursor is the worker. One connection can hand out several cursors, so one program can hold more than one result set open at a time. That split also explains which method sits where: execute() and the fetch methods belong to the cursor, while commit(), rollback() and close() finish work on the whole line and so belong to the connection.
Worked example. Every output printed in this chapter came from a real run against MySQL 8.0.41 with mysql-connector-python on Python 3.13. The demo server was on port 3307; on a normal machine leave port out and it defaults to 3306.
import mysql.connector
con = mysql.connector.connect(host="127.0.0.1", port=3307, user="root",
password="", database="cs12_python_sql_connectivity")
print("type(con) :", type(con).__name__)
print("is_connected() :", con.is_connected())
print("server version :", con.server_info)
cur = con.cursor()
print("type(cur) :", type(cur).__name__)
cur.execute("SELECT DATABASE()")
print("current db :", cur.fetchone())
cur.execute("SELECT Name, City, Salary FROM EMPLOYEE WHERE Dept='IT'")
print("column names :", [d[0] for d in cur.description])
for row in cur.fetchall():
print(row)
cur.close()
con.close()
print("after close :", con.is_connected())Real output:
type(con) : CMySQLConnection
is_connected() : True
server version : 8.0.41
type(cur) : CMySQLCursor
current db : ('cs12_python_sql_connectivity',)
column names : ['Name', 'City', 'Salary']
('Diya Nair', 'Kochi', Decimal('72000.00'))
('Rohan Verma', 'Pune', Decimal('65000.00'))
after close : FalseFour things to read off that output. First, fetchone() returned a tuple, not a plain value — ('cs12_python_sql_connectivity',) — so to print just the value you index it, cur.fetchone()[0]. Second, a DECIMAL column comes back as a Decimal object, not a float, which is why the salaries print as Decimal('72000.00'). Third, is_connected() flips to False the instant you close.
Fourth, the class names begin with C. The pip wheel ships an optional C extension, and when it is present connect() gives you a CMySQLConnection and a CMySQLCursor. Pass use_pure=True and you get the pure-Python MySQLConnection and MySQLCursor instead — the pair that textbooks quote. Everything in this chapter behaves identically on both except one detail, flagged in Section 2.
The EMPLOYEE table used everywhere in this chapter was itself created from Python — no phpMyAdmin, no command line:
import mysql.connector
con = mysql.connector.connect(host="127.0.0.1", port=3307, user="root", password="")
print("connected:", con.is_connected())
cur = con.cursor()
cur.execute("CREATE DATABASE cs12_python_sql_connectivity")
cur.execute("USE cs12_python_sql_connectivity")
cur.execute("""
CREATE TABLE EMPLOYEE (
EmpID INT PRIMARY KEY,
Name VARCHAR(25),
City VARCHAR(20),
Dept VARCHAR(15),
Salary DECIMAL(10,2)
)
""")
rows = [
(101, 'Aarav Sharma', 'Delhi', 'Sales', 58000),
(102, 'Diya Nair', 'Kochi', 'IT', 72000),
(103, 'Rohan Verma', 'Pune', 'IT', 65000),
(104, 'Ishita Banerjee','Kolkata', 'HR', 49000),
(105, 'Kabir Singh', 'Jaipur', 'Sales', 53000),
(106, 'Meera Iyer', 'Chennai', 'Accounts', 61000),
]
cur.executemany("INSERT INTO EMPLOYEE VALUES (%s,%s,%s,%s,%s)", rows)
con.commit()
print("rows inserted:", cur.rowcount)
cur.execute("SELECT * FROM EMPLOYEE")
for r in cur.fetchall():
print(r)
cur.close(); con.close()Real output — these six rows are the seed table, and every later example says whether it starts from them:
connected: True
rows inserted: 6
(101, 'Aarav Sharma', 'Delhi', 'Sales', Decimal('58000.00'))
(102, 'Diya Nair', 'Kochi', 'IT', Decimal('72000.00'))
(103, 'Rohan Verma', 'Pune', 'IT', Decimal('65000.00'))
(104, 'Ishita Banerjee', 'Kolkata', 'HR', Decimal('49000.00'))
(105, 'Kabir Singh', 'Jaipur', 'Sales', Decimal('53000.00'))
(106, 'Meera Iyer', 'Chennai', 'Accounts', Decimal('61000.00'))When connect() fails. Put it inside try ... except mysql.connector.Error so the user gets a sentence instead of a traceback. Two failures, both produced on purpose:
import mysql.connector
try:
con = mysql.connector.connect(host="127.0.0.1", port=3307, user="root",
password="wrongpassword",
database="cs12_python_sql_connectivity")
except mysql.connector.Error as e:
print("Error class :", type(e).__name__)
print("errno :", e.errno)
print("msg :", e.msg)Real output:
Error class : ProgrammingError
errno : 1045
msg : Access denied for user 'root'@'localhost' (using password: YES)Asking for a database that does not exist gives, on the same server, ProgrammingError with errno 1049 and the message Unknown database 'no_such_db_xyz'. Learn to recognise those two numbers. Between them, 1045 (wrong user or password) and 1049 (wrong database name) explain most of the "my program won't connect" problems in a school lab.
Wrong-object mistakes cost marks every single year. All four of these were triggered deliberately, and the messages below are exactly what Python printed on the default connection:
| What you wrote | What Python said |
|---|---|
cur.commit() | AttributeError: 'CMySQLCursor' object has no attribute 'commit' |
con.execute("SELECT 1") | AttributeError: 'CMySQLConnection' object has no attribute 'execute' |
cur.execute(...) after cur.close() | ProgrammingError: 2055: Cursor is not connected |
con.cursor() after con.close() | OperationalError: MySQL Connection not available. |
On a connection made with use_pure=True the same four mistakes give the same four errors with the un-prefixed class names — 'MySQLCursor' and 'MySQLConnection' — which is how most textbooks print them. The mistake is identical either way, so quote whichever your own machine shows.
Compress all four into one sentence and you will never mix them up: execute and fetch belong to the cursor; connect, commit, rollback and close belong to the connection.
- MySQL is a server listening on a port (3306 by default); Python is a client and needs the mysql-connector-python driver to reach it.
- Install it as mysql-connector-python but import it as mysql.connector — the two names differ.
- connect() returns the connection (the line) and cursor() returns the cursor (the worker). execute() and the fetch methods are cursor methods; commit(), rollback() and close() are connection methods.
- fetchone() always returns a tuple, so index it with [0]; a DECIMAL column arrives as a Decimal object, not a float.
- errno 1045 means wrong user or password and errno 1049 means unknown database — catch mysql.connector.Error instead of letting a traceback reach the user.
- With the C extension installed the objects are CMySQLConnection and CMySQLCursor; use_pure=True gives the MySQLConnection and MySQLCursor that textbooks name. Error messages quote whichever you are using.
