ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python 与 MySQL 数据库交互:获取插入后的自增 ID 深度解析与 TaoToken 配置实战

Python 与 MySQL 数据库交互:获取插入后的自增 ID 深度解析与 TaoToken 配置实战 1. 插入数据后拿不到自增 ID问题到底出在哪Python 与 MySQL 数据库交互时获取插入后的自增 ID 是个高频需求新增用户后要拿 user_id 去建权限记录新增订单后要拿 order_id 去写日志新增文章后要拿 article_id 去做重定向。这些场景都指向同一个动作——INSERT 成功后把数据库生成的那个 AUTO_INCREMENT 值取回来。但实际操作里很多人会踩到几类坑cursor.lastrowid 返回 None以为驱动坏了用了 executemany 批量插入发现只拿到一个 ID在连接池里多线程跑担心拿到的 ID 是别人的或者干脆用 SELECT LAST_INSERT_ID() 又写错会话拿到上一次的旧值。这篇聚焦 Python 通过 mysql-connector-python 或 PyMySQL 插入数据后获取 lastrowid 的完整链路覆盖 cursor.lastrowid、SELECT LAST_INSERT_ID() 与 executemany 场景差异。同时给出一套可复制的 settings.json / config.toml 骨架以及用 TaoToken 统一 Key 接入配置的方式让数据库凭据和模型调用凭据都走同一套管理思路。适合正在写后端 CRUD、做数据同步脚本或者刚接触 MySQL 自增主键的开发者。2. 前置准备TaoToken 统一 Key 与数据库凭据管理在写代码之前先把凭据管理这件事理清楚。很多教程让你把数据库密码硬编码在 Python 文件里这在本地跑 demo 没问题一旦进版本库就是事故。我的做法是数据库连接参数放配置文件模型调用凭据走 TaoToken 统一 Key两者都不进代码。TaoToken 在这里的角色是统一管理模型调用的 API Key。当你的 Python 脚本除了写 MySQL还要调用大模型做数据清洗、字段补全、内容摘要时把模型 Key 收敛到一处会省很多事。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。先拿到 Key进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建一个 Key。这个 Key 后面会写进配置文件供脚本读取。数据库这边先建库建表。表必须有 AUTO_INCREMENT 主键这是 lastrowid 能返回有意义值的前提CREATE DATABASE IF NOT EXISTS mydatabase DEFAULT CHARSET utf8mb4; USE mydatabase; CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL, registered_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;注意 ENGINEInnoDB。MyISAM 虽然也支持 AUTO_INCREMENT但事务和行锁行为不同现代项目一律用 InnoDB。3. 可复制配置settings.json 与 config.toml 骨架配置文件的作用是把「会变的东西」抽出来。下面给两份骨架选你顺手的格式即可。3.1 settings.json 骨架{ mysql: { host: 127.0.0.1, port: 3306, user: app_user, password: REPLACE_WITH_ENV_OR_SECRET, database: mydatabase, charset: utf8mb4, autocommit: false, connection_timeout: 10 }, taotoken: { base_url: https://taotoken.net/api, api_key: REPLACE_WITH_YOUR_TAOTOKEN_KEY, default_model: claude-sonnet-4-5 } }3.2 config.toml 骨架[mysql] host 127.0.0.1 port 3306 user app_user password REPLACE_WITH_ENV_OR_SECRET database mydatabase charset utf8mb4 autocommit false connection_timeout 10 [taotoken] base_url https://taotoken.net/api api_key REPLACE_WITH_YOUR_TAOTOKEN_KEY default_model claude-sonnet-4-5读取配置的代码JSON 用内置 jsonTOML 用 tomllibPython 3.11import json from pathlib import Path def load_settings(path: str settings.json) - dict: with Path(path).open(r, encodingutf-8) as f: return json.load(f) settings load_settings() DB_CONFIG settings[mysql] TAOTOKEN_CONFIG settings[taotoken]注意配置文件里的 password 和 api_key 建议用环境变量覆盖例如 os.environ.get(MYSQL_PASSWORD, settings[mysql][password])这样配置文件可以安全进仓库。4. 核心链路cursor.lastrowid 与 SELECT LAST_INSERT_ID()4.1 安装驱动pip install mysql-connector-python # 或者用 PyMySQL pip install pymysql两者在 lastrowid 行为上基本一致下面以 mysql-connector-python 为主PyMySQL 的差异会单独标注。4.2 单行插入并获取自增 IDimport mysql.connector def insert_user_and_get_id(db_config: dict, username: str, email: str): last_id None conn None cursor None try: conn mysql.connector.connect(**db_config) cursor conn.cursor() sql INSERT INTO users (username, email) VALUES (%s, %s) cursor.execute(sql, (username, email)) conn.commit() last_id cursor.lastrowid print(f插入成功username{username}, id{last_id}) except mysql.connector.Error as err: print(f插入失败: {err}) if conn and conn.is_connected(): conn.rollback() finally: if cursor: cursor.close() if conn and conn.is_connected(): conn.close() return last_id关键点cursor.lastrowid 在 cursor.execute() 之后即可读取不必等 commit。但如果你在 commit 之前又执行了另一条 INSERTlastrowid 会被覆盖成后一条的 ID。所以拿到 ID 后立刻存变量别拖。4.3 用 SELECT LAST_INSERT_ID() 作为对照cursor.execute(INSERT INTO users (username, email) VALUES (%s, %s), (username, email)) conn.commit() cursor.execute(SELECT LAST_INSERT_ID()) last_id cursor.fetchone()[0]LAST_INSERT_ID() 是连接级session 级的不同连接互不干扰所以并发下也是安全的。但它多一次往返查询且必须在同一个连接里紧接着执行。cursor.lastrowid 本质上是驱动帮你把这次查询的结果缓存下来了所以更省事。4.4 executemany 的差异这是最容易踩坑的地方。批量插入时rows [(u1, u1example.com), (u2, u2example.com), (u3, u3example.com)] cursor.executemany(INSERT INTO users (username, email) VALUES (%s, %s), rows) conn.commit() print(cursor.lastrowid) # 通常只返回第一行的 IDmysql-connector-python 在 executemany 后lastrowid 一般只反映第一行。如果你需要全部 ID有几种策略策略做法适用场景逐行插入循环 execute每次取 lastrowid行数少需要精确 ID应用层生成主键用 UUID 替代自增分布式、需要预知 ID插入后按条件查回用唯一字段 SELECT 回查有业务唯一键依赖连续自增取首 ID 后按行数推算单连接、无并发、innodb_autoinc_lock_mode0/1最后一种最脆弱不推荐。生产环境优先用「应用层生成主键」或「逐行插入」。5. 验证请求插入后 ID 校验的完整动作拿到 ID 不代表对。写一个校验函数用 ID 回查一次确认数据真的落库且字段匹配def verify_user_by_id(db_config: dict, user_id: int): with mysql.connector.connect(**db_config) as conn: with conn.cursor(dictionaryTrue) as cursor: cursor.execute( SELECT id, username, email, registered_at FROM users WHERE id %s, (user_id,) ) row cursor.fetchone() if row: print(f校验通过: {row}) else: print(f校验失败: 未找到 id{user_id}) return row跑一遍完整流程if __name__ __main__: new_id insert_user_and_get_id(DB_CONFIG, alice, aliceexample.com) if new_id: verify_user_by_id(DB_CONFIG, new_id)预期输出类似插入成功usernamealice, id1 校验通过: {id: 1, username: alice, email: aliceexample.com, registered_at: datetime.datetime(...)}如果校验返回 None说明 ID 拿到了但数据没落库八成是 commit 没执行或事务被回滚。6. 本篇常见错排查6.1 lastrowid 返回 None最常见原因表没有 AUTO_INCREMENT 主键或者你手动给自增列传了值。检查表结构SHOW CREATE TABLE users;确认 id 列带 AUTO_INCREMENT。另外如果 INSERT 因为唯一键冲突失败lastrowid 也可能是 None 或旧值务必先判断 execute 是否抛异常。6.2 executemany 后只拿到一个 ID这是驱动行为不是 bug。需要全部 ID 就改逐行插入或者改用 UUID 主键。别试图用 lastrowid 加行数推算并发下必错。6.3 多线程下 ID 串了只要每个线程用独立连接lastrowid 就是连接隔离的不会串。串的原因通常是多个线程共用一个 connection 或 cursor。连接池场景下确保从池里借出的连接在同一线程内用完即还。6.4 PyMySQL 的差异PyMySQL 同样支持 cursor.lastrowid行为一致。但 PyMySQL 默认 autocommitFalse忘记 commit 会导致数据不落库回查时找不到。另外 PyMySQL 连接参数用 charsetutf8mb4别写成 utf-8。6.5 时区与 DATETIME 对不上registered_at 用 DEFAULT CURRENT_TIMESTAMP 时取回的时间取决于 MySQL 服务器时区。如果 Python 侧显示差 8 小时在连接参数里加 time_zone08:00或在 MySQL 配置里统一时区。6.6 连接超时导致插入中断长事务或网络抖动会让连接断掉此时 commit 抛异常lastrowid 不可信。给连接加 connection_timeout并在 except 里显式 rollback。重试逻辑要放在应用层别在驱动层硬扛。7. 把模型调用也接进同一套配置当你的脚本需要在插入前用大模型生成 username 或 email 模板时可以直接读同一份配置里的 TaoToken 段import json, urllib.request def call_model(prompt: str, cfg: dict) - str: req urllib.request.Request( f{cfg[base_url]}/v1/messages, datajson.dumps({ model: cfg[default_model], max_tokens: 256, messages: [{role: user, content: prompt}] }).encode(), headers{ Content-Type: application/json, x-api-key: cfg[api_key], anthropic-version: 2023-06-01 }, methodPOST ) with urllib.request.urlopen(req, timeout30) as resp: return json.loads(resp.read())[content][0][text]这样数据库凭据和模型凭据都在 settings.json 里换环境只改一份文件。想先验证模型是否通可以去模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 手动发一条消息确认 Key 有效。如果是要长期跑编码任务或 Agent 流程Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 会更合适。接入细节和参数说明在文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里Claude Code 相关配置参考 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 。8. 收尾几个我实际用下来的习惯第一lastrowid 拿到后立刻赋值给局部变量别在中间插任何其他 INSERT。第二executemany 场景一律不依赖 lastrowid要么逐行要么 UUID。第三校验动作别省尤其是跨服务写库时回查一次能挡掉大部分「以为成功其实回滚」的问题。第四配置文件里的敏感字段用环境变量覆盖settings.json 只留占位符。第五连接用完即关with 语句能省掉一堆 finally 样板代码。把这些串起来Python 与 MySQL 的自增 ID 获取链路就稳了。
返回列表