分类
联系方式
  1. 新浪微博
  2. E-mail

MongoEngine

介绍

MongoEngine 是一个从对象(Object)到文档(Document)的映射器,基于 MongoDB。

安装方式:python -m pip install -U mongoengine

MongoEngine 的底层基于 PyMongo driver,适配 MongoDB 多种版本。

MongoEngine 有一个特点,它跟 Django ORM 非常像,如果你有后者的使用经验,将会感到很熟悉。

连接 MongoDB

通过以下方法连接 MongoDB 数据库:

from mongoengine import *

connect('tumblelog')

其中:

  • 默认是 MongoDB 运行在本地的场景,因此只需要提供一个数据库名
  • 如果 MongoDB 是远程访问的,需要特别注意远程访问的安全性问题,我从新闻中看到有很多攻击事件,都是攻击者通过扫描器,扫描公网上暴露的 MongoDB 数据库,轻松窃取到数据

如果要远程访问:

from mongoengine import *

connect(host='mongodb://192.168.0.1:27017/db_name')

多数据库

MongoEngine 支持同时连接多个 MongoEngine 数据库:

connect(alias='user-db-alias', db='user-db')
connect(alias='book-db-alias', db='book-db')
connect(alias='users-books-db-alias', db='users-books-db')

class User(Document):
    name = StringField()

    meta = {'db_alias': 'user-db-alias'}

class Book(Document):
    name = StringField()

    meta = {'db_alias': 'book-db-alias'}

class AuthorBooks(Document):
    author = ReferenceField(User)
    book = ReferenceField(Book)

    meta = {'db_alias': 'users-books-db-alias'}

定义 Document

在 MongoDB 中,可粗略认为 Document 相当于 RDBMS 中的 row(行)。document 存储于 collections 中,是 shchemaless 的。


定义一个 Document:

class User(Document):
    email = StringField(required=True)
    first_name = StringField(max_length=50)
    last_name = StringField(max_length=50)

其中:

  • Document 包名:mongoengine.document
  • fields 包名:mongoengine.fields

需要注意的是,MongoDB 是一个 NoSQL 数据库,是 Schema 无关的,这里定义的 Schema 并不会传给 MongoDB。定义的 Schema 也是有约束力的,但是只限于框架之上的应用层。

形成对比的是,在 SQL ORM 中,这里的 Schema 将会严格映射到数据库表中。

Document 间的引用关系:

class Post(Document):
    title = StringField(max_length=120, required=True)
    author = ReferenceField(User)

DynamicDocument

DynamicDocument 比 Document 扩充的地方在于,对于 DynamicDocument,除了设置声明字段外,也可以对未声明的字段进行复赋值,更加灵活:

from mongoengine import *

class Page(DynamicDocument):
    title = StringField(max_length=200, required=True)

# Create a new page and add tags
>>> page = Page(title='Using MongoEngine')
>>> page.tags = ['mongodb', 'mongoengine']
>>> page.save()

>>> Page.objects(tags='mongoengine').count()
>>> 1

EmbeddedDocument

围绕 Document 有很多灵活的用法,这里先简单介绍 EmbeddedDocument。这种 Document 的作用是,它自身不单独向数据库中存,而是作为其它的 Document 的字段,随对方一起存。这样就形成了只有单独实体,但数据库中没有单独 Collection。示例:

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))

CASCADE

对于引用关系而言(在关系型数据库中称为外键),删除表的时候需要考虑一个问题,那些通过外键引用它的其它表该怎么处理呢?

CASCADE 就是一种处理方式,即把我(本表)删除的时候,把那些对我有引用的数据也一并删除了吧。

MongoEngine 中也支持这种方式,通过 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))

Field

Field 是 Document 中的字段,MongoEngine 支持多种字段类型。

field 默认都是可不传的,如果需要强制赋值,需要指定 required 为 True。

支持的 Field 类型:

  • BinaryField
  • BooleanField
  • ComplexDateTimeField
  • DateTimeField
  • DecimalField
  • DictField
  • DynamicField
  • EmailField
  • EmbeddedDocumentField
  • EmbeddedDocumentListField
  • EnumField
  • FileField
  • FloatField
  • GenericEmbeddedDocumentField
  • GenericReferenceField
  • GenericLazyReferenceField
  • GeoPointField
  • ImageField
  • IntField
  • ListField
  • LongField
  • MapField
  • ObjectIdField
  • ReferenceField
  • LazyReferenceField
  • SequenceField
  • SortedListField
  • StringField
  • URLField
  • UUIDField
  • PointField
  • LineStringField
  • PolygonField
  • MultiPointField
  • MultiLineStringField
  • MultiPolygonField

Reference fields

实现对其它 Document 的引用。在第一个参数中传入类名:

class User(Document):
    name = StringField()

class Page(Document):
    content = StringField()
    author = ReferenceField(User)

john = User(name="John Smith")
john.save()

post = Page(content="Test Page")
post.author = john
post.save()

自我引用的话,第一个参数传 'self':

class Employee(Document):
    name = StringField()
    boss = ReferenceField('self')
    profile_page = ReferenceField('ProfilePage')

class ProfilePage(Document):
    content = StringField()

引用删除关系

默认 MongoDB 是不校验一致性的,不会进行关联删除(CASCADE)。

MongoEngine 单独添加了关联删除的支持,开启方式:

class ProfilePage(Document):
    employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE)

当 Employee 删除的时候,指向它的所有 ProfilePage 也会一并被删除。除了 CASCADE 外,可取值有:

取值 作用
mongoengine.DO_NOTHING 什么也不做。

会导致数据库不一致。

与被删除 Document 存咋关联关系的其它 Document 没有关联删除。

mongoengine.DENY 对于一个 Document,如果有其它 Document 对其有引用,不允许删除
mongoengine.NULLIFY 将对其有引用的 Document 对应字段置空
mongoengine.CASCADE 关联删除
mongoengine.PULL 多对多场景下,从 ListField 中删除对应的 ReferenceField

ListField

MongoDB 支持存储列表,参数中要在给出一个 Field,表示列表中数据的类型:

class Page(Document):
    tags = ListField(StringField(max_length=50))

多对多关系

Reference fields 和 ListField 结合使用,可实现多对多关系:

class User(Document):
    name = StringField()

class Page(Document):
    content = StringField()
    authors = ListField(ReferenceField(User))

bob = User(name="Bob Jones").save()
john = User(name="John Smith").save()

Page(content="Test Page", authors=[bob, john]).save()
Page(content="Another Page", authors=[john]).save()

# Find all pages Bob authored
Page.objects(authors__in=[bob])

# Find all pages that both Bob and John have authored
Page.objects(authors__all=[bob, john])

# Remove Bob from the authors for a page.
Page.objects(id='...').update_one(pull__authors=bob)

# Add John to the authors for a page.
Page.objects(id='...').update_one(push__authors=john)

DateTimeField

date_modified = DateTimeField(
    default=datetime.datetime.utcnow)

添加数据

保存

简单保存:

ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save()

更新

对有 id 的对象,进行 save,会更新数据。

有 id 的对象包括:

  • 上面通过 save 新创建的
  • 从数据库中请求回来的

访问数据

遍历

for post in TextPost.objects:
    print(post.content)

TextPost.objects 跟 Django 实在是太像了。

过滤

for post in Post.objects(tags='mongodb'):
    print(post.title)

计数

num_posts = Post.objects(tags='mongodb').count()
print('Found {} posts with tag "mongodb"'.format(num_posts))

网络资源

项目首页