4. Python数据库连接
好,咱们进入正题。配方数据存到文件里终究不是长久之计,真要上点规模的项目,数据库是绕不开的。Python 连 MySQL,最常用的就是 PyMySQL 这个库。今天我就带你把它捋一遍。
4.1 为什么选 PyMySQL?
Python 连 MySQL 的库有好几个,比如 mysql-connector-python、MySQLdb。但我个人习惯用 PyMySQL。原因很简单:纯 Python 实现,安装省心,不用折腾 C 编译器。我在项目中遇到过好几次,换台机器环境不一样,MySQLdb 死活装不上,换成 PyMySQL 一行命令搞定。
核心优势:
- 纯 Python,无外部依赖
- 兼容 Python 3.x 和 PyPy
- API 设计跟 MySQLdb 几乎一样,迁移成本低
4.2 安装 PyMySQL
安装没什么好说的,pip 一把梭。
pip install pymysql
嗯,这里要注意。如果你用的是 Python 3.x,而且项目里同时用了 MySQLdb 的代码,可以在项目入口加一句:
import pymysql
pymysql.install_as_MySQLdb()
这样所有原本用 MySQLdb 的代码就能直接跑,不用改。我有个老项目就是这么干的,省了不少事。
4.3 建立连接
连数据库,说白了就是告诉 PyMySQL 四样东西:地址、端口、用户名、密码。再加一个库名。
import pymysql
connection = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='your_password',
database='recipe_db',
charset='utf8mb4'
)
你想想看,这跟填快递单差不多。收件地址是 host,门牌号是 port,收件人是 user,密码是 password。charset 用 utf8mb4,别问为什么,问就是它能存 emoji。配方表里万一有人用表情符号做备注呢?
我曾经踩过的坑:密码里如果有特殊字符,比如 @、#、$,一定要用引号包好,或者用 URL 编码。有一次我密码里有个 %,直接写进去解析报错,查了半天才发现是密码的问题。
4.4 游标对象
连接建好了,怎么执行 SQL?靠游标。你可以把游标理解成一个遥控器,连接是电视,游标是遥控器上的按钮。按一下,电视就执行一个动作。
cursor = connection.cursor()
游标有好几种类型,最常用的是默认游标和字典游标。
| 游标类型 | 返回结果 | 适用场景 |
|---|---|---|
| 默认游标 | 元组 | 简单查询,字段顺序固定 |
| 字典游标 | 字典 | 字段名不固定,或需要按名取值 |
我个人习惯用字典游标。为什么?因为元组取数据得靠下标,比如 row[0]、row[1],时间长了根本记不住哪个字段对应哪个下标。字典游标用 row['id']、row['name'],代码可读性高得多。
cursor = connection.cursor(pymysql.cursors.DictCursor)
4.5 执行 SQL 语句
游标拿到手,接下来就是执行 SQL。分两种:查询和修改。
4.5.1 查询
sql = "SELECT id, name, ingredients FROM recipes WHERE category = '蛋糕'"
cursor.execute(sql)
results = cursor.fetchall()
for row in results:
print(row['name'], row['ingredients'])
fetchall() 一次性拿回所有结果。如果数据量大,可以用 fetchone() 一条一条拿,或者 fetchmany(size) 分批拿。
小技巧:数据量超过几千条时,别用 fetchall(),内存会炸。用 fetchmany() 每次拿 100 条,循环处理,稳得很。
4.5.2 修改(插入、更新、删除)
sql = "INSERT INTO recipes (name, ingredients, steps) VALUES (%s, %s, %s)"
data = ('提拉米苏', '马斯卡彭、手指饼干、咖啡', '1. 混合...')
cursor.execute(sql, data)
connection.commit()
这里有个关键点:一定要 commit。execute 只是把 SQL 发给 MySQL,MySQL 执行完默认不会真的写盘。你得告诉它:行了,确认吧。不 commit 的话,数据就丢了。
我曾经犯过的错:刚用 PyMySQL 那会儿,写了个插入脚本,跑完一看数据库啥也没有。查了半天,发现忘了 commit。从那以后,我写修改操作都会在 execute 后面紧跟一句 connection.commit(),养成肌肉记忆。
4.6 参数化查询:防 SQL 注入
拼接 SQL 字符串是新手最容易犯的错误。比如这样:
# 千万别这么写
name = "蛋糕' OR '1'='1"
sql = f"SELECT * FROM recipes WHERE name = '{name}'"
为什么?因为用户输入里可能藏着恶意 SQL。上面那个例子,拼接完就变成了:
SELECT * FROM recipes WHERE name = '蛋糕' OR '1'='1'
所有数据全给你查出来。更狠的还能删表。所以,永远用参数化查询。
sql = "SELECT * FROM recipes WHERE name = %s"
cursor.execute(sql, (name,))
PyMySQL 会自动处理转义,把输入当纯文本,不参与 SQL 解析。安全又省心。
4.7 完整流程:从连接到关闭
一个标准的数据库操作流程,应该是这样的:
- 建立连接
- 获取游标
- 执行 SQL
- 提交事务(修改操作)
- 关闭游标
- 关闭连接
写成代码就是:
import pymysql
try:
conn = pymysql.connect(
host='localhost',
user='root',
password='123456',
database='recipe_db',
charset='utf8mb4'
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = "INSERT INTO recipes (name, category) VALUES (%s, %s)"
cursor.execute(sql, ('红丝绒蛋糕', '蛋糕'))
conn.commit()
print(f"插入成功,ID: {cursor.lastrowid}")
except pymysql.Error as e:
print(f"数据库错误: {e}")
conn.rollback()
finally:
cursor.close()
conn.close()
注意 finally 块里的 close。不管有没有异常,游标和连接都得关。不然连接池会被占满,别人连不上了。
4.8 知识结构图
下面这张图把 PyMySQL 的核心流程串起来了。你照着这个思路走,基本不会出错。
4.9 常见问题速查
| 问题 | 原因 | 解决办法 |
|---|---|---|
| pymysql.err.OperationalError: (1045, ...) | 用户名或密码错误 | 检查 user 和 password |
| pymysql.err.OperationalError: (1049, ...) | 数据库不存在 | 先 CREATE DATABASE |
| pymysql.err.ProgrammingError: (1064, ...) | SQL 语法错误 | 检查 SQL 语句,注意占位符用 %s |
| 数据没写进去 | 忘了 commit | 加 connection.commit() |
| 中文乱码 | charset 没设 utf8mb4 | 连接时指定 charset='utf8mb4' |