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 完整流程:从连接到关闭

一个标准的数据库操作流程,应该是这样的:

  1. 建立连接
  2. 获取游标
  3. 执行 SQL
  4. 提交事务(修改操作)
  5. 关闭游标
  6. 关闭连接

写成代码就是:

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 的核心流程串起来了。你照着这个思路走,基本不会出错。

PyMySQL 数据库操作核心流程 1. 安装 PyMySQL 2. 建立连接 3. 获取游标 4. 执行 SQL 5a. 查询: fetch 5b. 修改: commit 6. 关闭游标和连接 关键提醒 • 修改操作必须 commit • 用参数化查询防注入 • 用完记得 close

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'