MongoEngine中文入门教程
翻译整理自官方文档 http://docs.mongoengine.org/tutorial.html
本教程通过创建一个简单的微博客(tumblelog)来学习MongoEngine。
1 安装MongoEngine
只需要使用pip安装即可,命令:
$ python -m pip install mongoengine
使用connect功能函数。第一个参数是要连接的数据库的名称:
from mongoengine import *
connect('tumblelog')
默认情况下,MongoEngine假定实例在端口27017的localhost端口运行。如果MongoDB在其他地方运行,则应提供host
和port
参数 connect()
:
connect('project', host='192.168.1.35', port=12345)
如果数据库需要身份验证,应提供username
,password
和authentication_source
参数:
connect('project', username='webapp', password='pwd123', authentication_source='admin')
URI风格的连接,也支持-只需提供该URI作为host
给 connect()
:
connect('project1', host='mongodb://localhost/database_name')
2 定义文件
MongoDB是无模式的,但是为文档定义模式可以帮助消除涉及错误类型或缺少字段的错误,还可以像传统ORM一样,在我们的文档上定义实用程序方法 。
本例中需要的信息类型有:
-
用户
-
帖子
-
标签
-
评论
用户User:定义User
可能具有的字段以及它们可能存储的数据类型:
class User(Document):
email = StringField(required=True)
first_name = StringField(max_length=50)
last_name = StringField(max_length=50)
看起来类似于在常规ORM中如何定义表的结构。关键区别在于,该架构永远不会传递给MongoDB,只会在应用程序级别强制实施,从而使将来的更改易于管理。同样,用户文档将存储在MongoDB集合中,而不是表中。
帖子Post: 区别于关系型数据库,们会将所有帖子存储在一个集合中,每种帖子类型只会存储所需的字段。例如如果以后要添加视频帖子,只需开始使用支持视频帖子所需的新字段即可。这很好地符合了面向对象的继承原则。我们可以认为 Post
作为基类,并且TextPost
,ImagePost
和 LinkPost
作为子类Post
。实际上,MongoEngine开箱即用地支持这种建模-需要做的就是通过allow_inheritance
在中将True设置为True来打开继承meta
:
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User)
meta = {'allow_inheritance': True}
class TextPost(Post): # 文本
content = StringField()
class ImagePost(Post): # 图像
image_path = StringField()
class LinkPost(Post): # 链接
link_url = StringField()
使用ReferenceField
对象存储对帖子作者的引用 。这些与传统ORM中的外键字段相似,并且在保存时会自动转换为引用,在加载时会取消引用。
将标签直接作为字符串存储在帖子中。
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User)
tags = ListField(StringField(max_length=30))
评论Comment:作为嵌入式文档列表 直接存储在发布文档上。
class Comment(EmbeddedDocument):
content = StringField()
name = StringField(max_length=120)
相应地:
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User)
tags = ListField(StringField(max_length=30))
comments = ListField(EmbeddedDocumentField(Comment))
处理删除:该ReferenceField
对象使用关键字 reverse_delete_rule来处理删除规则。
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User, reverse_delete_rule=CASCADE)
tags = ListField(StringField(max_length=30))
comments = ListField(EmbeddedDocumentField(Comment))
3 向Tumblelog中添加数据
创建User对象;
ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save()
或使用属性语法定义:
ross = User(email='ross@example.com')
ross.first_name = 'Ross'
ross.last_name = 'Lawley'
ross.save()
同样的添加新用户john。
之后添加几篇帖子:
post1 = TextPost(title='Fun with MongoEngine', author=john)
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
post1.tags = ['mongodb', 'mongoengine']
post1.save()
post2 = LinkPost(title='MongoEngine Documentation', author=ross)
post2.link_url = 'http://docs.mongoengine.com/'
post2.tags = ['mongoengine']
post2.save()
4 数据访问
每个文档类(即直接或间接继承的任何类Document
)都有一个objects
属性,该属性用于访问与该类关联的数据库集合中的文档。如获取帖子标题:
for post in Post.objects:
print(post.title)
访问特定类型:
for post in TextPost.objects:
print(post.content)
使用TextPost的objects
属性仅返回使用创建的文档TextPost
。实际上,这里有一个更通用的规则:objects
任何子类的属性Document
仅查找使用该子类或其子类之一创建的文档。
显示所有帖子,仅显示与每个帖子的特定类型相对应的信息:
for post in Post.objects:
print(post.title)
print('=' * len(post.title))
if isinstance(post, TextPost):
print(post.content)
if isinstance(post, LinkPost):
print('Link: {}'.format(post.link_url))
打印每个帖子的标题,如果是文本帖子,则显示内容,如果是链接帖子,则显示“链接“。
通过标签搜索帖子:
返回带有标签“ mongodb”的帖子:
for post in Post.objects(tags='mongodb'):
print(post.title)
也有可用方法QuerySet
的对象,允许不同的结果被返回,例如,要求 first()
在objects
属性将返回一个单一的文件,通过所提供的查询第一个匹配。聚合函数也可以用于QuerySet
对象:
num_posts = Post.objects(tags='mongodb').count()
print('Found {} posts with tag "mongodb"'.format(num_posts))
入门教程结束,若需要更深入的了解推荐参阅官方完整用户指南。
附录1:
上例代码:
from mongoengine import *
class User(Document):
email = StringField(required=True)
first_name = StringField(max_length=50)
last_name = StringField(max_length=50)
class Comment(EmbeddedDocument):
content = StringField()
name = StringField(max_length=120)
class Post(Document):
title = StringField(max_length=120, required=True)
author = ReferenceField(User, reverse_delete_rule=CASCADE)
tags = ListField(StringField(max_length=30))
comments = ListField(EmbeddedDocumentField(Comment))
meta = {'allow_inheritance': True}
class TextPost(Post):
content = StringField()
class ImagePost(Post):
image_path = StringField()
class LinkPost(Post):
link_url = StringField()
def createDBs():
ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save()
john = User(email='john@example.com', first_name='John', last_name='O.o').save()
post1 = TextPost(title='Fun with MongoEngine', author=john)
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
post1.tags = ['mongodb', 'mongoengine']
post1.save()
post2 = LinkPost(title='MongoEngine Documentation', author=ross)
post2.link_url = 'http://docs.mongoengine.com/'
post2.tags = ['mongoengine']
post2.save()
#createDBs() 第一次运行先创建
connect('tumblelog')
for post in Post.objects:
print(post.title)
print('=' * len(post.title))
if isinstance(post, TextPost):
print(post.content)
if isinstance(post, LinkPost):
print('Link: {}'.format(post.link_url))
num_posts = Post.objects(tags='mongoengine').count()
print('Found {} posts with tag "mongodb"'.format(num_posts))
附录2 常用MongoDB命令:
docker exec -it mongo mongo docker进入MongoDB
show dbs 显示所有数据库
use XXX 进入某数据库
show tables 显示所有表格
db.YYY.find().pretty() 显示某表的内容
db.dropDatabase() 删除数据库