第 4-1 课:PostgreSQL 与 Psycopg 入门
一、本课定位
你已经掌握 SQL、事务和 Java 数据库开发,因此本课不再从表、字段和增删改查讲起,而是集中回答一个问题:Python 程序怎样安全地连接 PostgreSQL 并执行 SQL?
Python 数据库 API(Database API,DB-API)规定了数据库驱动的通用操作方式。Psycopg 3 是 PostgreSQL 的 Python 驱动,本课会把它与 JDBC 逐项对照。
二、本课目标
完成本课后,你将能够:
- 解释 DB-API、Psycopg 和 PostgreSQL 的关系;
- 使用本地 TOML 文件保存数据库连接配置;
- 使用
psycopg.connect()创建连接; - 使用游标执行只读 SQL 并取得结果;
- 使用参数化查询传递数据;
- 使用
with自动释放连接和游标; - 对照 JDBC 理解 Python 数据库代码;
- 识别连接失败、依赖缺失和参数占位符错误。
三、前置知识
本课默认已经掌握:
- PostgreSQL 数据库地址、端口、数据库、用户名和密码的含义;
SELECT基础语法;- JDBC 的
Connection、PreparedStatement和ResultSet; - Python 函数、异常、上下文管理器和文件读取基础。
本课只连接专用练习数据库。不要连接生产数据库,也不要使用具有创建用户、删除数据库等高权限的账号。
四、DB-API、Psycopg 与 JDBC
DB-API 是 Python 数据库驱动遵循的接口约定,不是一个需要单独安装的框架。不同数据库有不同驱动,但常见操作方式比较统一。
| Java/JDBC | Python/Psycopg | 作用 |
|---|---|---|
| PostgreSQL JDBC Driver | Psycopg 3 | 与 PostgreSQL 通信 |
DriverManager.getConnection() |
psycopg.connect() |
创建数据库连接 |
Connection |
Connection |
表示一次数据库会话 |
PreparedStatement |
Cursor.execute(sql, params) |
执行参数化 SQL |
ResultSet |
Cursor 和 fetchone() 等方法 |
读取查询结果 |
try-with-resources |
with |
自动释放资源 |
SQLException |
psycopg.Error |
表示数据库访问错误 |
Psycopg 的游标(Cursor)同时承担“执行 SQL”和“读取结果”的职责。它不是数据库界面中的鼠标光标。
五、准备远程练习数据库
建议为课程准备:
- 一个独立数据库,例如
python_course; - 一个专用账号,例如
python_student; - 只授予课程所需权限;
- 不与生产环境或其他重要测试数据共用。
本课不使用环境变量,也不要求把所有信息拼成数据库连接串,而是把连接参数分字段写入 TOML 配置:
[postgresql]
host = "数据库主机"
port = 5432
dbname = "数据库名"
user = "用户名"
password = "密码"
connect_timeout = 10
仓库中的 config.example.toml 只包含占位内容,可以提交 Git。实际配置写入同目录的 config.toml,该文件已经被项目 .gitignore 排除。
TOML(Tom's Obvious Minimal Language)是一种结构化配置格式。Python 3.11 及以上版本内置 tomllib,读取 TOML 不需要安装额外依赖,也不会修改操作系统或当前进程的环境变量。
六、安装 Psycopg 3
你当前使用 Conda 的 base 环境,可以直接安装 Psycopg:
conda install -n base -c conda-forge "psycopg>=3,<4" psycopg-c
安装完成后验证:
python -c "import psycopg; print(psycopg.__version__)"
如果以后改用 Python 虚拟环境,也可以使用 requirements.txt 安装。无论使用哪种方式,导入时都写 import psycopg,不是 import psycopg3。
七、创建本地 TOML 配置
进入本课目录,复制配置模板:
Copy-Item .\config.example.toml .\config.toml
然后只在本地 config.toml 中填写真实的主机、端口、数据库名、用户名和密码。程序通过 Python 文件自身的位置寻找配置,因此从项目根目录或课程目录启动都可以。
7.1 为什么选择 TOML
- Python 3.13 可以直接使用内置
tomllib; - 字段和类型清楚,端口可以保持整数;
- 不需要污染环境变量;
- 比 XML 简洁,比 YAML 少一个第三方解析依赖;
- 配置节结构与 Java 项目的 YAML、Properties 配置思路相近。
7.2 代码硬编码可以怎么写
从技术上可以直接构造字典:
database_config = {
"host": "数据库主机",
"port": 5432,
"dbname": "数据库名",
"user": "用户名",
"password": "密码",
}
这能帮助理解 psycopg.connect() 接收哪些参数,但真实密码一旦硬编码(Hard Coding)就可能进入 Git 历史。本课程标准示例使用 config.toml;如自行尝试硬编码,只能放在不提交的个人练习文件中。
八、完整示例
示例文件为 connection_example.py。核心结构如下:
database_config = load_database_config(CONFIG_PATH)
with psycopg.connect(**database_config) as connection:
with connection.cursor() as cursor:
cursor.execute(
"SELECT current_database(), current_user, %s::text",
("Psycopg 连接成功",),
)
database_name, user_name, message = cursor.fetchone()
示例只读取当前数据库名和当前用户,不创建表、不修改数据。
8.1 为什么参数使用 %s
Psycopg 使用 %s 表示值参数,即使参数是整数也仍然使用 %s。参数值通过 execute() 的第二个参数单独传入:
cursor.execute("SELECT %s::text", (message,))
不要使用 f-string、字符串拼接或 % 运算符把数据直接写进 SQL:
# 错误示例:数据被直接拼进 SQL,可能产生 SQL 注入。
cursor.execute(f"SELECT '{message}'")
8.2 单个参数为什么有逗号
(message,)
这是只有一个元素的元组。写成 (message) 只是在字符串外加括号,不是元组。
8.3 with 做了什么
- 离开游标的
with时关闭游标; - 离开连接的
with时结束事务并关闭连接; - 正常离开连接块时提交当前事务;
- 块内出现异常时回滚当前事务。
本课执行的是只读查询,但仍要建立正确的资源和事务管理习惯。
九、运行方法
确认 config.toml 已经创建并填写完成,然后在项目根目录运行:
python .\04_数据库\4_1_PostgreSQL与Psycopg入门\connection_example.py
正常情况下会看到类似结果:
连接成功。
当前数据库:python_course
当前用户:python_student
参数化查询结果:Psycopg 连接成功
数据库名和用户名应以你的远程练习环境为准。
十、关键执行顺序
main()调用load_database_config(CONFIG_PATH);Path根据当前 Python 文件的位置定位config.toml;tomllib.load()读取[postgresql]配置节;- 缺少文件、配置节或必填字段时主动抛出中文
RuntimeError; psycopg.connect(**database_config)把字典展开为连接参数;connection.cursor()创建游标;cursor.execute()将 SQL 和查询参数分别交给驱动;cursor.fetchone()读取一行结果;- 两层
with依次释放游标和连接; main()输出结果,并分别处理配置异常和数据库异常。
十一、常见错误
11.1 ModuleNotFoundError: No module named 'psycopg'
含义:当前 Python 环境没有安装 Psycopg。
处理:激活 .venv,再使用本课的 requirements.txt 安装依赖。可以运行 python -m pip show psycopg 检查安装位置。
11.2 未找到 config.toml
含义:本课目录中还没有实际配置文件。
处理:把 config.example.toml 复制为 config.toml 并填写连接参数。不要直接把真实信息写进示例模板。
11.3 TOML 格式错误
含义:配置不符合 TOML 语法,例如字符串缺少引号或同一个键重复出现。
处理:对照 config.example.toml 检查配置节、等号、引号和字段名。
11.4 connection refused 或连接超时
含义:程序无法到达数据库地址和端口。
检查数据库服务、主机名、端口、防火墙、白名单和 VPN,不要先假设一定是密码错误。
11.5 password authentication failed
含义:服务器已经收到连接,但用户名或密码校验失败。
检查 config.toml 中的账号、密码和数据库名。分字段传参时无需手动拼接 URL,也避免了连接串中特殊字符编码问题。
11.6 execute() 参数写错
下面两种写法都不符合本课要求:
cursor.execute("SELECT '%s'", (message,))
cursor.execute("SELECT %s", message)
占位符外不要加引号,参数序列只有一个值时要写成 (message,)。
11.7 使用 fetchone() 却没有处理空结果
fetchone() 在没有数据时可能返回 None。本课查询必然返回一行,所以可以直接解包;以后查询业务表时必须判断空结果。
十二、课堂练习
打开 practice.py,按照注释完成练习。练习要求你独立完成:
- 使用
Path定位本地config.toml; - 使用
tomllib读取 PostgreSQL 配置; - 使用两层
with管理连接和游标; - 执行包含两个参数的只读查询;
- 使用
fetchone()保存并输出结果; - 分别捕获配置读取异常和数据库访问异常。
练习仍然只执行只读 SQL,不创建、修改或删除远程数据。
十三、本课小结
- DB-API 是 Python 数据库驱动的通用接口约定;
- Psycopg 3 是 PostgreSQL 的 Python 驱动;
- Psycopg 的基础层次与 JDBC 相似;
Connection表示数据库会话,Cursor负责执行 SQL 和读取结果;- SQL 与参数必须分开传递;
with用于可靠地结束事务和释放资源;- 数据库密码保存在被 Git 忽略的本地 TOML 配置中,不写入环境变量或源码。
十四、验收标准
- 能说明 DB-API、Psycopg 和 PostgreSQL 的关系;
- 能说出 Psycopg 与 JDBC 的主要对象对应关系;
- 能复制模板并通过本地
config.toml配置连接参数; connection_example.py可以连接练习数据库并输出三项查询结果;practice.py使用参数化查询,没有拼接 SQL;- 连接和游标都通过
with管理; - 能区分网络不可达、认证失败和依赖缺失;
- 程序没有读取、设置或修改环境变量;
- 未把
config.toml或真实数据库连接信息写入 Git 跟踪文件。