尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python操作MongoDB实战:pymongo增删改查教程与最佳实践

Python操作MongoDB实战:pymongo增删改查教程与最佳实践 很多同学在学 Python 的过程中到了“操作数据库”这一步就开始头疼MySQL 要写 SQLOracle 要配驱动Redis 又要学一套新命令。其实如果你想用最少的代码把数据存起来、查出来、改一改、删一删MongoDB 是一个非常友好的选择尤其是配合 Python 的 pymongo 库很多操作就像在操作字典一样自然。本文会从 MongoDB 是什么讲起逐个拆解 pymongo 的核心操作包含完整的 Python 代码示例和运行结果说明。无论你是刚开始学 Python 的初学者还是已经在项目里用 MongoDB 做数据存储的开发者都能从这份教程里找到可以直接复用的内容。1. MongoDB 是什么为什么要用 pymongo1.1 MongoDB 解决什么问题MongoDB 是一个 NoSQL 文档型数据库它不要求你提前设计表结构也不要求每一条数据都有完全相同的字段。它存储的基本单位是“文档”这个文档的格式非常接近 Python 里的 dict 或 JSON。比如{ name: 张三, age: 25, city: 北京, tags: [python, mongodb] }这种设计带来的直观好处是业务模型变化时不需要像关系型数据库那样频繁执行 ALTER TABLE。今天想给用户加一个phone字段直接插入新数据就行历史数据不受影响。1.2 pymongo 是什么pymongo 是 MongoDB 官方提供的 Python 驱动库它把 Python 对象和 MongoDB 文档做了自然映射。你在 Python 里写的{name: 张三}可以直接插入数据库从数据库查询出来的文档也可以直接当成字典来操作。1.3 MongoDB 与 MySQL 的直观对比对比项MongoDBMySQL数据模型文档型类似 JSON关系型表结构表/集合集合Collection表Table记录文档Document行Row字段字段Field列Column主键_id一般用自增主键适合场景快速迭代、数据结构灵活强事务、复杂查询、报表统计1.4 适合用 MongoDB 的场景爬虫抓取的数据存储字段不稳定时非常省心。用户画像、消息记录、日志数据等半结构化数据。产品原型阶段需求变化快不想频繁维护数据库表结构。需要水平扩展的读写场景。2. 环境准备与版本说明2.1 安装 MongoDB 服务要操作 MongoDB首先需要一个 MongoDB 服务实例。你可以选择本地安装 MongoDB Community Server。使用云厂商提供的 MongoDB 服务。使用 Docker 启动一个临时 MongoDB 容器。安装时版本不需要写死以当前官方稳定版为准。本文的示例并不过度依赖某一个特定版本重点在于演示 pymongo 的操作方式。Windows 下安装完成后一般可以作为 Windows 服务自动启动。Linux 下安装完成后可以用 systemctl 或直接运行 mongod 命令启动。2.2 启动 MongoDB 服务本地开发时最简单的验证方式是打开命令行执行mongod --dbpath D:\mongodb-data如果看到类似Waiting for connections的日志说明服务已经启动成功。2.3 安装 pymongo建议在虚拟环境中安装。在项目目录下执行pip install pymongo如果你需要更方便地处理 MongoDB 的 BSON 数据可以一起安装pip install pymongo dnspythondnspython主要用于 MongoDB Atlas 等连接串中包含主机名解析的场景本地连接不强制需要。安装完成后可以写一个小脚本验证版本import pymongo print(pymongo.__version__)2.4 本篇使用环境本文的代码基于 Python 3.xpymongo 4.x 版本。不同大版本的 API 有细微差异但最常用的插入、查询、更新、删除方法基本是一致的。3. 连接 MongoDB 与核心对象3.1 MongoClient 连接数据库pymongo 的核心连接类是MongoClient。它既负责建立连接也负责后续所有数据库和集合操作。from pymongo import MongoClient # 方式 1裸参数 client MongoClient(mongodb://localhost:27017/) # 方式 2指定主机和端口 client MongoClient(hostlocalhost, port27017) # 方式 3带认证信息 # client MongoClient(mongodb://用户名:密码localhost:27017/数据库名?authSourceadmin)在本地开发环境使用默认端口 27017 即可。生产环境请务必设置用户名密码并限制访问来源 IP。3.2 数据库、集合、文档三层结构MongoDB 的逻辑结构是数据库Database集合Collection类似 MySQL 的表文档Document类似 MySQL 的一行如果指定的数据库或集合不存在pymongo 不会立刻报错而是在第一次插入数据时自动创建。# 获取数据库如果不存在会在写入时自动创建 db client[test_db] # 获取集合如果不存在会在写入时自动创建 user_collection db[users]也可以使用属性式访问db client.test_db user_collection db.users两种方式效果相同。我个人更推荐用[]方式因为当数据库名或集合名包含特殊字符时[]更安全。3.3 查看数据库和集合连接成功后可以查看当前服务下有哪些数据库和集合from pymongo import MongoClient client MongoClient(mongodb://localhost:27017/) # 查看所有数据库名称 print(client.list_database_names()) db client[test_db] # 查看当前数据库下所有集合名称 print(db.list_collection_names())新装的 MongoDB 环境通常会显示admin、config、local这几个系统数据库它们不需要手动操作。4. pymongo 增删改查完整实战下面我们用一个“用户管理”的例子完整演示增删改查。假设我们要存储用户信息包含姓名、年龄、城市、标签。4.1 插入单条文档使用insert_one方法插入一条文档from pymongo import MongoClient client MongoClient(mongodb://localhost:27017/) db client[test_db] collection db[users] user { name: 张三, age: 25, city: 北京, tags: [python, mongodb] } result collection.insert_one(user) print(插入的文档 id, result.inserted_id)运行结果类似插入的文档 id 64f9b2a1d2e4f5a6b7c8d9e0注意insert_one会给文档自动添加_id字段类型是ObjectId。如果你自己在文档中指定了_id则使用你指定的值。4.2 插入多条文档需要插入多条数据时使用insert_manyusers [ {name: 李四, age: 30, city: 上海, tags: [java, spring]}, {name: 王五, age: 22, city: 广州, tags: [python, django]}, {name: 赵六, age: 35, city: 深圳, tags: [go, mysql]}, ] result collection.insert_many(users) print(插入的文档 id 列表, result.inserted_ids)4.3 查询单条文档使用find_one查询满足条件的第一条文档from pymongo import MongoClient client MongoClient(mongodb://localhost:27017/) collection client[test_db][users] doc collection.find_one({name: 张三}) print(doc) print(姓名, doc[name]) print(年龄, doc[age])返回结果是一个字典可以直接用键名取值。4.4 查询多条文档使用find查询所有满足条件的文档。find返回的是一个Cursor对象可以迭代cursor collection.find() for doc in cursor: print(doc[name], doc[age], doc[city])如果想只返回部分字段可以在find的第二个参数中指定投影cursor collection.find({}, {_id: 0, name: 1, city: 1}) for doc in cursor: print(doc)1表示返回该字段0表示不返回。_id默认会返回如果不需要可以显式指定_id: 0。4.5 按条件查询MongoDB 的查询条件非常接近自然语言。比如查询年龄大于等于 30 的用户cursor collection.find({age: {$gte: 30}}) for doc in cursor: print(doc[name], doc[age])常用比较操作符操作符含义示例$gt大于{age: {$gt: 25}}$gte大于等于{age: {$gte: 30}}$lt小于{age: {$lt: 25}}$lte小于等于{age: {$lte: 25}}$ne不等于{city: {$ne: 北京}}$in在列表中{city: {$in: [北京, 上海]}}$regex正则匹配{name: {$regex: ^张}}注意这里的花括号、美元符号不是格式装饰而是 MongoDB 查询语法的必要部分。查询条件本质上也是一个文档。4.6 更新单条文档更新操作最常用的是$set它只修改指定字段不会覆盖整条文档。result collection.update_one( {name: 张三}, {$set: {age: 26}} ) print(匹配条数, result.matched_count) print(修改条数, result.modified_count)如果不使用$set直接传入新文档会存在覆盖整条文档的风险# 危险示例会覆盖整个文档 result collection.update_one( {name: 张三}, {age: 100} )这种写法在 pymongo 中是合法的但会把原文档替换成只包含一个age字段的文档其他字段会丢失。日常开发中要非常谨慎。4.7 批量更新使用update_many可以一次性更新所有满足条件的文档result collection.update_many( {city: 北京}, {$set: {city: 北京朝阳}} ) print(修改条数, result.modified_count)批量更新前建议先通过count_documents或find_one确认条件是否符合预期避免误操作。4.8 删除文档删除操作同样分为delete_one和delete_many# 删除第一条 name 为 张三 的文档 result collection.delete_one({name: 张三}) print(删除条数, result.deleted_count) # 删除所有年龄小于 25 的文档 result collection.delete_many({age: {$lt: 25}}) print(删除条数, result.deleted_count)在测试环境练习删除操作没有问题但在生产环境请务必先备份数据并且确认删除条件。4.9 统计文档数量使用count_documents统计文档条数count collection.count_documents({}) print(总文档数, count) count collection.count_documents({age: {$gte: 30}}) print(年龄大于等于30的文档数, count)count_documents({})的第一个参数是查询条件如果传空字典表示统计集合内所有文档。5. 排序、分页与去重5.1 排序查询结果需要排序时使用sort方法。sort的参数是字段和方向方向用pymongo.ASCENDING或1表示升序pymongo.DESCENDING或-1表示降序。from pymongo import MongoClient, ASCENDING, DESCENDING collection client[test_db][users] # 按年龄升序 cursor collection.find().sort(age, ASCENDING) for doc in cursor: print(doc[name], doc[age]) # 按年龄降序 cursor collection.find().sort(age, DESCENDING)多个字段排序可以传入列表cursor collection.find().sort([ (age, DESCENDING), (name, ASCENDING) ])5.2 分页分页通常配合skip和limit使用# 跳过前 10 条取 5 条 cursor collection.find().skip(10).limit(5) for doc in cursor: print(doc)不过要注意数据量较大时skip跳过的数据过多会导致查询变慢。生产环境更推荐基于_id或某个排序字段做游标分页。5.3 distinct 去重查询某个字段有哪些不同的取值cities collection.distinct(city) print(cities)这个操作类似 MySQL 的SELECT DISTINCT city FROM users。6. 简单聚合操作6.1 什么是 aggregationMongoDB 的聚合框架可以完成分组、求和、求平均等统计操作。pymongo 中通过aggregate方法传入一个管道列表来实现。比如统计每个城市的用户人数pipeline [ {$group: {_id: $city, total: {$sum: 1}}} ] result collection.aggregate(pipeline) for doc in result: print(doc)运行结果类似{_id: 北京, total: 1} {_id: 上海, total: 1} {_id: 广州, total: 1} {_id: 深圳, total: 1}$city中的美元符号表示引用文档中的字段。$sum: 1表示每匹配一条文档就加 1。再比如统计每个城市的平均年龄pipeline [ {$group: {_id: $city, avg_age: {$avg: $age}}} ] result collection.aggregate(pipeline) for doc in result: print(doc)聚合功能很强大但本篇只做入门展示后续可以专门深入学习。7. 索引的基本概念与应用7.1 为什么需要索引MongoDB 的查询如果没走索引会进行全集合扫描数据量变大后性能会明显下降。给常用的查询字段建立索引可以大幅提升查询速度。7.2 创建索引使用create_index方法创建索引collection.create_index([(age, ASCENDING)])创建复合索引collection.create_index([(city, ASCENDING), (age, DESCENDING)])创建唯一索引可以保证字段不重复collection.create_index([(name, ASCENDING)], uniqueTrue)7.3 查看索引for index in collection.list_indexes(): print(index)7.4 删除索引collection.drop_index(age_1)这里的age_1是索引名称可以在list_indexes中查看。索引不是越多越好。每个索引都会增加写入时的维护成本建议只为高频查询字段建立索引。8. 常见问题与排查思路问题现象常见原因解决思路连接报错ServerSelectionTimeoutErrorMongoDB 服务未启动或连接地址端口错误先确认 mongod 进程是否存在再确认端口是否被占用连接报错AuthenticationFailed用户名密码或认证库错误确认连接串中的数据库名和authSource参数中文写入后查询正常但显示乱码终端编码问题MongoDB 本身存储 UTF-8检查控制台编码是否支持中文insert_one后没有返回 id异常被吞掉或连接中断检查是否捕获了异常检查网络状态更新后数据字段丢失更新时没有使用$set直接传了字段文档更新操作优先使用$set数据量大时查询很慢缺少索引或skip跳过大量数据为查询字段建立索引优化分页方案批量插入几千条数据速度慢循环调用insert_one改用insert_many除了表格中的问题还有一个非常容易踩的坑在代码里循环执行update_one或delete_one。这种操作虽然功能正常但每一条都要和数据库进行一轮网络交互性能很差。应该优先看能不能用update_many、delete_many或批量写操作替代。9. 最佳实践与工程建议9.1 连接统一管理项目中不要到处创建MongoClient。建议在模块中创建一次连接全局复用。MongoClient 本身是线程安全的可以在多线程环境中共享。# db.py from pymongo import MongoClient client MongoClient(mongodb://localhost:27017/) db client[test_db] users db[users]其他文件导入from db import users9.2 使用上下文管理或异常处理数据库操作可能因为网络问题、权限问题、数据格式问题抛出异常至少要在业务入口捕获异常并记录日志from pymongo import MongoClient from pymongo.errors import PyMongoError client MongoClient(mongodb://localhost:27017/) collection client[test_db][users] data { name: 测试用户, age: 20 } try: result collection.insert_one(data) print(插入成功, result.inserted_id) except PyMongoError as e: print(MongoDB 操作失败, e) finally: client.close()9.3 生产环境的安全与权限MongoDB 服务不要用默认端口直接暴露到公网。给应用创建独立账号只授权需要的数据库。不要使用管理员账号连接应用数据库。定期备份数据至少保证有可恢复的备份文件。9.4 批量操作优先插入多条数据时优先使用insert_many更新删除多条数据时优先使用update_many和delete_many。这能显著减少网络交互次数。9.5 写入字段做统一管理虽然 MongoDB 不要求字段固定但同一个业务集合中建议字段命名保持一致。例如用户信息统一用name、age、city不要一会儿用name一会儿用user_name否则后期维护成本会很高。9.6 注意时区问题MongoDB 默认存储 UTC 时间。如果你保存 Python 的datetime对象查询出来需要自行转换时区或者使用pymongo.utcnow()后再做转换。不要直接存储没有时区信息的字符串否则后续统计时间时会很痛苦。9.7 关于删除和更新操作任何删除、批量更新操作在测试环境和生产环境执行前都要先回答三个问题影响的数据范围是什么条件能否用find_one或count_documents预先验证是否有备份能否回滚养成这个习惯可以有效避免误操作。10. 总结继续深入的方向到这里你已经掌握了 pymongo 操作 MongoDB 的基础闭环连接服务、创建数据库和集合、执行增删改查、排序分页、聚合统计、创建索引、处理常见异常。这套能力已经足够支撑很多 Python 项目的存储需求。如果接下来想继续深入可以从这几个方向入手MongoDB 聚合框架的完整语法尤其是$lookup关联查询和$unwind展开数组。索引底层原理比如复合索引的最左前缀规则。MongoDB 的复制集和分片集群理解高可用架构。在 Web 框架FastAPI、Django、Flask中结合 pymongo 做项目实战。使用 Motor 这个异步 MongoDB 驱动在高并发场景下替代 pymongo。学习数据库最好的方式不是把文档从头读到尾而是动手写一个小项目。可以先把本文的示例代码跑通再试着改造一个自己的笔记系统或者给爬虫程序加上数据存储。遇到不能理解的问题时多打印doc查看数据结构很多困惑会自然解开。
返回列表