4.1 使用 PyMongo 进行 MongoDB 基础操作
在我们使用 MongoDB 编写 Web 应用之前,我们需要了解如何在 Python 中使用 MongoDB。在这一节,你将学会如何使用 PyMongo 连接 MongoDB 数据库,然后学习如何使用 pymongo 在 MongoDB 集合中创建、取出和更新文档。
PyMongo 是一个简单的包装 MongoDB 客户端 API 的 Python 库。你可以在 http://api.mongodb.org/python/current/ 下载获得。一旦你安装完成,打开一个 Python 解释器,然后跟随下面的步骤。
4.1.1 创建连接
首先,你需要导入 PyMongo 库,并创建一个到 MongoDB 数据库的连接。
>>> import pymongo
>>> conn = pymongo.Connection("localhost", 27017)
前面的代码向我们展示了如何连接运行在你本地机器上默认端口(27017)上的 MongoDB 服务器。如果你正在使用一个远程 MongoDB 服务器,替换 localhost 和 27017 为合适的值。你也可以使用 MongoDB URI 来连接 MongoDB,就像下面这样:
>>> conn = pymongo.Connection(
... "mongodb://user:password@staff.mongohq.com:10066/your_mongohq_db")
前面的代码将连接 MongoHQ 主机上的一个名为 your_mongohq_db 的数据库,其中 user 为用户名,password 为密码。你可以在 http://www.mongodb.org/display/DOCS/Connections 中了解更多关于 MongoDB URI 的信息。
一个 MongoDB 服务器可以包括任意数量的数据库,而 Connection 对象可以让你访问你连接的服务器的任何一个数据库。你可以通过对象属性或像字典一样使用对象来获得代表一个特定数据库的对象。如果数据库不存在,则被自动建立。
>>> db = conn.example or: db = conn['example']
一个数据库可以拥有任意多个集合。一个集合就是放置一些相关文档的地方。我们使用 MongoDB 执行的大部分操作(查找文档、保存文档、删除文档)都是在一个集合对象上执行的。你可以在数据库对象上调用 collection_names 方法获得数据库中的集合列表。
>>> db.collection_names()
[]
当然,我们还没有在我们的数据库中添加任何集合,所以这个列表是空的。当我们插入第一个文档时,MongoDB 会自动创建集合。你可以在数据库对象上通过访问集合名字的属性来获得代表集合的对象,然后调用对象的 insert 方法指定一个 Python 字典来插入文档。比如,在下面的代码中,我们在集合 widgets 中插入了一个文档。因为 widgets 集合并不存在,MongoDB 会在文档被添加时自动创建。
>>> widgets = db.widgets or: widgets = db['widgets'] (see below)
>>> widgets.insert({"foo": "bar"})
ObjectId('4eada0b5136fc4aa41000000')
>>> db.collection_names()
[u'widgets', u'system.indexes']
(system.indexes 集合是 MongoDB 内部使用的。处于本章的目的,你可以忽略它。)
在之前展示的代码中,你既可以使用数据库对象的属性访问集合,也可以把数据库对象看作一个字典然后把集合名称作为键来访问。比如,如果 db 是一个 pymongo 数据库对象,那么 db.widgets 和 db['widgets']同样都可以访问这个集合。
4.1.2 处理文档
MongoDB 以文档的形式存储数据,这种形式有着相对自由的数据结构。MongoDB 是一个"无模式"数据库:同一个集合中的文档通常拥有相同的结构,但是 MongoDB 中并不强制要求使用相同结构。在内部,MongoDB 以一种称为 BSON 的类似 JSON 的二进制形式存储文档。PyMongo 允许我们以 Python 字典的形式写和取出文档。
为了在集合中 创建一个新的文档,我们可以使用字典作为参数调用文档的 insert 方法。
>>> widgets.insert({"name": "flibnip", "description": "grade-A industrial flibnip", "quantity": 3})
ObjectId('4eada3a4136fc4aa41000001')
既然文档在数据库中,我们可以使用集合对象的 find_one 方法来取出文档。你可以通过传递一个键为文档名、值为你想要匹配的表达式的字典来告诉 find_one 找到 一个特定的文档。比如,我们想要返回文档名域 name 的值等于 flibnip 的文档(即,我们刚刚创建的文档),可以像下面这样调用 find_oen 方法:
>>> widgets.find_one({"name": "flibnip"})
{u'description': u'grade-A industrial flibnip',
u'_id': ObjectId('4eada3a4136fc4aa41000001'),
u'name': u'flibnip', u'quantity': 3}
请注意_id 域。当你创建任何文档时,MongoDB 都会自动添加这个域。它的值是一个 ObjectID,一种保证文档唯一的 BSON 对象。你可能已经注意到,当我们使用 insert 方法成功创建一个新的文档时,这个 ObjectID 同样被返回了。(当你创建文档时,可以通过给_id 键赋值来覆写自动创建的 ObjectID 值。)
find_one 方法返回的值是一个简单的 Python 字典。你可以从中访问独立的项,迭代它的键值对,或者就像使用其他 Python 字典那样修改值。
>>> doc = db.widgets.find_one({"name": "flibnip"})
>>> type(doc)
<type 'dict'>
>>> print doc['name']
flibnip
>>> doc['quantity'] = 4
然而,字典的改变并不会自动保存到数据库中。如果你希望把字典的改变保存,需要调用集合的 save 方法,并将修改后的字典作为参数进行传递:
>>> doc['quantity'] = 4
>>> db.widgets.save(doc)
>>> db.widgets.find_one({"name": "flibnip"})
{u'_id': ObjectId('4eb12f37136fc4b59d000000'),
u'description': u'grade-A industrial flibnip',
u'quantity': 4, u'name': u'flibnip'}
让我们在集合中添加更多的文档:
>>> widgets.insert({"name": "smorkeg", "description": "for external use only", "quantity": 4})
ObjectId('4eadaa5c136fc4aa41000002')
>>> widgets.insert({"name": "clobbasker", "description": "properties available on request", "quantity": 2})
ObjectId('4eadad79136fc4aa41000003')
我们可以通过调用集合的 find 方法来获得集合中所有文档的列表,然后迭代其结果:
>>> for doc in widgets.find():
... print doc
...
{u'_id': ObjectId('4eada0b5136fc4aa41000000'), u'foo': u'bar'}
{u'description': u'grade-A industrial flibnip',
u'_id': ObjectId('4eada3a4136fc4aa41000001'),
u'name': u'flibnip', u'quantity': 4}
{u'description': u'for external use only',
u'_id': ObjectId('4eadaa5c136fc4aa41000002'),
u'name': u'smorkeg', u'quantity': 4}
{u'description': u'properties available on request',
u'_id': ObjectId('4eadad79136fc4aa41000003'),
u'name': u'clobbasker',
u'quantity': 2}
如果我们希望获得文档的一个子集,我们可以在 find 方法中传递一个字典参数,就像我们在 find_one 中那样。比如,找到那些 quantity 键的值为 4 的集合:
>>> for doc in widgets.find({"quantity": 4}):
... print doc
...
{u'description': u'grade-A industrial flibnip',
u'_id': ObjectId('4eada3a4136fc4aa41000001'),
u'name': u'flibnip', u'quantity': 4}
{u'description': u'for external use only',
u'_id': ObjectId('4eadaa5c136fc4aa41000002'),
u'name': u'smorkeg',
u'quantity': 4}
最后,我们可以使用集合的 remove 方法从集合中删除一个文档。remove 方法和 find、find_one 一样,也可以使用一个字典参数来指定哪个文档需要被删除。比如,要删除所有 name 键的值为 flipnip 的文档,输入:
>>> widgets.remove({"name": "flibnip"})
列出集合中的所有文档来确认上面的文档已经被删除:
>>> for doc in widgets.find():
... print doc
...
{u'_id': ObjectId('4eada0b5136fc4aa41000000'),
u'foo': u'bar'}
{u'description': u'for external use only',
u'_id': ObjectId('4eadaa5c136fc4aa41000002'),
u'name': u'smorkeg', u'quantity': 4}
{u'description': u'properties available on request',
u'_id': ObjectId('4eadad79136fc4aa41000003'),
u'name': u'clobbasker',
u'quantity': 2}
4.1.3 MongoDB 文档和 JSON
使用 Web 应用时,你经常会想采用 Python 字典并将其序列化为一个 JSON 对象(比如,作为一个 AJAX 请求的响应)。由于你使用 PyMongo 从 MongoDB 中取出的文档是一个简单的字典,你可能会认为你可以使用 json 模块的 dumps 函数就可以简单地将其转换为 JSON。但,这还有一个障碍:
>>> doc = db.widgets.find_one({"name": "flibnip"})
>>> import json
>>> json.dumps(doc)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
[stack trace omitted]
TypeError: ObjectId('4eb12f37136fc4b59d000000') is not JSON serializable
这里的问题是 Python 的 json 模块并不知道如何转换 MongoDB 的 ObjectID 类型到 JSON。有很多方法可以处理这个问题。其中最简单的方法(也是我们在本章中采用的方法)是在我们序列化之前从字典里简单地删除_id 键。
>>> del doc["_id"]
>>> json.dumps(doc)
'{"description": "grade-A industrial flibnip", "quantity": 4, "name": "flibnip"}'
一个更复杂的方法是使用 PyMongo 的 json_util 库,它同样可以帮你序列化其他 MongoDB 特定数据类型到 JSON。我们可以在 http://api.mongodb.org/python/current/api/bson/json_util.html 了解更多关于这个库的信息。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论