Mongoengine 简明教程

MongoEngine - Quick Guide

MongoEngine - MongoDB

在过去十年中,NoSQL 数据库变得越来越受欢迎。在当今实时 Web 应用程序的世界中,移动设备和嵌入式设备产生了大量数据。传统的关联数据库(如 Oracle、MySQL 等)不适用于字符串。此类数据的处理过程也存在困难,因为它们具有固定和预定义的模式,且不可扩展。NoSQL 数据库拥有灵活的模式,并以分布式的方式存储在大量社区服务器上。

NoSQL 数据库根据数据的组织方式进行分类。MongoDB 是一款流行的文档存储 NoSQL 数据库。MongoDB 数据库的基本组成部分被称为文档。文档是由 JSON 格式中存储的一组键值对。在一个集合中存储了多个文档。一个集合可以看作是任何关系数据库中的一张表,而一个文档则是表中的一行。不过需要指出的是,由于 MongoDB 无模式,一个集合中每份文档中的键值对数量不一定相同。

MongoDB 是由 MongoDB Inc. 开发的。它是一个通用的、基于分布式文档的数据库。它提供企业版和社区版。可在 https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.6-signed.msi 下载适用于 Windows 操作系统的最新版本社区版。

在您选择的文件夹中安装 MongoDB,并使用以下命令启动服务器 −

D:\mongodb\bin>mongod

服务器现已准备好接受端口 27017 上的传入连接请求。MongoDB 数据库存储在 bin/data 目录中。可以通过上述命令中的 –dbpath 选项更改此位置。

在另一个命令终端中,使用以下命令启动 MongoDB 控制台 −

D:\mongodb\bin>mongo

MongoDB 提示类似于我们在 MySQL 或 SQLite 终端中通常看到的提示。所有数据库操作(例如创建数据库、插入文档、更新和删除以及检索文档)都可以在控制台中完成。

E:\mongodb\bin>mongo
MongoDB shell version v4.0.6
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0d848b11-acf7-4d30-83df-242d1d7fa693") }
MongoDB server version: 4.0.6
---
>

正在使用的默认数据库为 test。

> db
Test

使用 'use' 命令可将任何其他数据库设置成当前数据库。如果指定的数据库不存在,则会创建新的数据库。

> use mydb
switched to db mydb

请参阅我们在 https://www.tutorialspoint.com/mongodb/index.htm 上的关于 MongoDB 的详细教程。

MongoEngine - MongoDB Compass

MongoDB 还开发了一款用于处理 MongoDB 数据库的 GUI 工具。它被称为 MongoDB Compass。这是一款无需手动编写查询即可执行所有 CRUD 操作的便利工具。它有助于执行诸多活动,如索引编制、文档验证等。

可从 https://www.mongodb.com/download-center/compass 下载 MongoDB Compass 的社区版,并启动 MongoDBCompassCommunity.exe (确保在启动 Compass 之前 MongoDB 服务器正在运行)。通过提供正确的 host 和 port 号连接到本地服务器。

databases

如下列出目前可用的所有数据库 −

new database

单击 + 按钮(在左面板底部显示)可创建新数据库。

name of database

从列表中选择数据库的名称,并选择一个集合,如下所示 −

csv or json file

您可以直接添加文档,或者从 CSV 或 JSON 文件中导入。

insert document

从添加数据下拉菜单中选择插入文档。

documents added

添加的文档将以 JSON、列表或表格形式显示 −

relational database

请注意,正如关系数据库中的表具有主键一样,MongoDB 数据库中的文档具有一个自动生成的特殊键称为“ _id ”。

MongoDB Inc. 为连接到 MongoDB 数据库提供了一个 Python 驱动程序。它称为 PyMongo ,其用法类似于标准 SQL 查询。

安装 PyMongo 模块后,我们需要 MongoClient 类的对象才能与 MongoDB 服务器交互。

<<< from pymongo import MongoClient
<<< client=MongoClient()

使用以下语句创建新数据库 −

db=client.mydatabase

对该数据库上的 CRUD 操作使用 insert_one()(或 insert_many())、find()、update() 和 delete() 方法进行。PyMongo 库的详细讨论可 https://www.tutorialspoint.com/python_data_access/python_mongodb_introduction.htm 获得。

但是,除非将 Python 的用户定义对象转换为 MongoDB 的数据类型,否则它不能存储在数据库中。这就是我们需要 MongoEngine 库的地方。

MongoEngine - Object Document Mapper

MongoDB 是基于文档的数据库。每个文档都是字段和值的 JSON 类似表示。MongoDB 中的文档大致等效于 RDBMS 表中的行(MongoDB 等效的表是集合)。尽管 MongoDB 不强制使用任何预定义架构,但文档中的字段对象具有一定数据类型。MongoDB 数据类型与 Python 的主数据类型非常相似。如果必须存储 Python 的用户定义类的对象,则必须手动将其属性解析为等效的 MongoDB 数据类型。

MongoEngine 在 PyMongo 上提供了一个便捷的抽象层,并将 Document 类的每个对象映射到 MongoDB 数据库中的一个文档。MongoEngine API 是由 Hary Marr 在 2013 年 8 月制定的。MongoEngine 的最新版本是 0.19.1。

MongoEngine 之于 MongoDB,正如 SQLAlchemy 之于 RDBMS 数据库。MongoEngine 库提供了一个 Document 类,该类用作定义自定义类的基础。该类的属性形成 MongoDB 文档的字段。Document 类定义执行 CRUD 操作的方法。在后续主题中,我们将学习如何使用它们。

MongoEngine - Installation

要使用 MongoEngine,您需要已安装 MongoDB,并且 MongoDB 服务器应按前面所述运行。

安装 MongoEngine 的最简单方法是使用 PIP 安装程序。

pip install mongoengine

如果您的 Python 安装未安装 Setuptools,则必须从 https://github.com/MongoEngine/mongoengine 下载 MongoEngine 并运行以下命令 −

python setup.py install

MongoEngine 具有以下依赖项 −

  1. pymongo>=3.4

  2. six>=1.10.0

  3. dateutil>=2.1.0

  4. pillow>=2.0.0

为了验证正确的安装,请运行 import 命令并检查版本,如下所示 −

>>> import mongoengine
>>> mongoengine.__version__
'0.19.1'

Connecting to MongoDB Database

如前所述,你应当首先使用 mongod 命令启动 MongoDB 服务器。

MongoEngine 提供 connect() 函数以连接到 mongodb 服务器的正在运行的实例。

from mongoengine import connect
connect(‘mydata.db’)

默认情况下,MongoDB 服务器在 localhost 上和 27017 端口上运行。若要自定义,你应当向 connect() 提供 host 和 port 参数 −

connect('mydata.db', host='192.168.1.1', port=12345)

如果数据库需要身份验证,则应当提供其凭据,例如 username、password 和 authentication_source 参数。

connect('mydata.db', username='user1', password='***', authentication_source='admin')

MongoEngine 还支持 URI 样式的连接,而不是 IP 地址。

connect('mydata.db', host='mongodb://localhost/database_name')

connect() 函数有另一个可选的参数,称为 replicaset。MongoDB 是分布式数据库。存储在某一台服务器中的数据通常在许多服务器实例中复制,以确保高可用性。MongoDB 中的 replica set(副本集)是在其上维护相同数据集的 mongod 进程组。Replica set 是所有生产部署的基础。

connect(host='mongodb://localhost/dbname?replicaSet=rs-name')

定义了以下副本集方法:

rs.add()

向 replica set 添加一个成员。

rs.conf()

返回 replica set 配置文档。

rs.freeze()

阻止当前成员在一段时间内作为 primary 寻求选举。

rs.initiate()

初始化新的 replica set。

rs.reconfig()

通过应用新的 replica set 配置对象来重新配置 replica set。

rs.remove()

从 replica set 中移除一个成员。

MongoEngine 还允许连接多个数据库。你需为每个数据库提供唯一的别名。例如,以下代码将 Python 脚本连接到两个 MongoDB 数据库。

connect(alias='db1', db='db1.db')
connect(alias='db2', db='db2.db')

MongoEngine - Document Class

MongoEngine 被称为 ODM(@(s0))。MongoEngine 定义了 Document 类。这是一个基类,其继承的类用于定义存储在 MongoDB 数据库中的一系列文档的结构和属性。此子类的每个对象形成数据库中 Collection 中的 Document。

此 Document 子类中的属性是由各种 Field 类的对象组成的。以下是一个典型的 Document 类的示例 −

from mongoengine import *
class Student(Document):
   studentid = StringField(required=True)
   name = StringField(max_length=50)
   age = IntField()
   def _init__(self, id, name, age):
      self.studentid=id,
      self.name=name
      self.age=age

这类似于 SQLAlchemy ORM 中的模型类。默认情况下,数据库中 Collection 的名称是 Python 类名,采用小写字母。然而,可在 Document 类的 meta 属性中指定不同的集合名称。

meta={collection': 'student_collection'}

现在,声明此类的对象并调用 save() 方法以将文档存储在数据库中。

s1=Student('A001', 'Tara', 20)
s1.save()

MongoEngine - Dynamic Schema

MongoDB 数据库的优点之一是支持动态架构。若要创建支持动态架构的类,可从 DynamicDocument 基类进行子类化。以下是具有动态架构的 Student 类−

>>> class student(DynamicDocument):
... name=StringField()

第一步是要先添加 Document 。

>>> s1=student()
>>> s1.name="Tara"
>>> connect('mydb')
>>> s1.save()

现在另一个属性添加到第二个文档中并保存。

>>> s2=student()
>>> setattr(s2,'age',20)
>>> s2.name='Lara'
>>> s2.save()

在数据库中,学生集合将显示两个具有动态架构的文档。

dynamic schema

文档类的元字典可以通过指定 max_documents 和 max_size 使用限幅集合。

max_documents − 集合中允许存储的最大文档数。

max_size − 集合的最大大小(以字节为单位)。max_size 由 MongoDB 在内部以及 mongoengine 在之前舍入为 256 的下一个倍数。

如果没有指定 max_size 但指定了 max_documents ,则 max_size 的默认值为 10485760 字节(10MB)。

文档类的其他参数如下所列 −

objects

在访问时延迟创建的 QuerySet 对象。

cascade_save()

以递归方式保存文档上的任何引用和泛型引用。

clean()

在运行验证之前用于执行文档级别数据清除的挂钩。

create_index()

根据需要创建给定的索引。

drop_collection()

从数据库中删除与该文档类型关联的整个集合。

from_json()

将 json 数据转换为 Document 实例。

modify()

以原子方式更新数据库中的文档并使用更新的版本重新加载文档对象。

pk

Get the primary key.

save()

将 Document 保存到数据库。如果文档已存在,则将对其进行更新,否则将创建该文档。返回已保存的对象实例。

delete()

从数据库中删除当前文档。

insert()

Performs bulk insert operation.

MongoEngine - Fields

一个 MongoEngine 文档类具有一个或多个属性。每个属性都是 Field 类的对象。BaseField 是所有字段类型的基类。BaseField 类构造函数具有以下参数−

BaseField(db_field, required, default, unique, primary_key)

db_field 表示数据库字段的名称。

required 参数决定该字段的值是否必需,默认为 false。

default 参数包含该字段的默认值

unique 参数默认值为 false。如果要使该字段对每个文档具有唯一值,则将其设置为 true。

primary_key 参数默认为 false。True 使该字段成为主键。

从 BaseField 派生了许多 Field 类。

Numeric Fields

IntField (32 位整数)、 LongField (64 位整数)、FloatField(浮点数)字段构造函数具有 min_value 和 max_value 参数。

还有 DecimalField 类。该字段对象的 value 是一个可以指定其精度的小数。为 DecimalField 类定义了以下参数:

DecimalField(min_value, max_value, force_string, precision, rounding)

min_value

specifies minimum acceptable value

max_value

指定字段可以具有的最大值

force_string

如果为 True,则该字段的值将存储为字符串

precision

将浮点表示限制为数字位数

rounding

数字按照以下预定义常量进行舍入 decimal.ROUND_CEILING(向无穷大)decimal.ROUND_DOWN(向零)decimal.ROUND_FLOOR(向 - 无穷大)decimal.ROUND_HALF_DOWN(到最接近,舍入后为零)decimal.ROUND_HALF_EVEN(到最接近,舍入后为最接近的偶数)decimal.ROUND_HALF_UP(到最接近,舍入后远离零)decimal.ROUND_UP(远离零)decimal.ROUND_05UP(远离零,如果向零舍入后的最后一位数字为 0 或 5;否则向零)

Text Fields

StringField 对象可以存储任何 Unicode 值。可以在构造函数中指定字符串的 min_length 和 max_length。 URLField 对象是 StringField,能够将输入验证为 URL。 EmailField 将字符串验证为有效的电子邮件表示形式。

StringField(max-length, min_length)
URLField(url_regex)
EmailField(domain_whiltelist, allow_utf8_user, allow_ip_domain)

domain_whitelist 参数包含您不支持的无效域列表。如果设置为 True,allow_utf8_user 参数允许字符串包含 UTF8 字符作为电子邮件的一部分。allow_ip_domain 参数默认为 false,但如果为 true,则它可以是有效的 IPV4 或 IPV6 地址。

以下示例使用数字和字符串字段:

from mongoengine import *
connect('studentDB')
class Student(Document):
   studentid = StringField(required=True)
   name = StringField()
   age=IntField(min_value=6, max-value=20)
   percent=DecimalField(precision=2)
   email=EmailField()
s1=Student()
s1.studentid='001'
s1.name='Mohan Lal'
s1.age=20
s1.percent=75
s1.email='mohanlal@gmail.com'
s1.save()

当执行上述代码时,student 集合将显示如下所示的文档:

document

ListField

这种类型的字段会包装任何标准字段,从而允许将多个对象用作数据库中的列表对象。该字段可以与 ReferenceField 一起使用来实现一对多关系。

来自上述示例的学生文档类被修改为如下所示:

from mongoengine import *
connect('studentDB')
class Student(Document):
   studentid = StringField(required=True)
   name = StringField(max_length=50)
   subjects = ListField(StringField())
s1=Student()
s1.studentid='A001'
s1.name='Mohan Lal'
s1.subjects=['phy', 'che', 'maths']
s1.save()

添加的文档以 JSON 格式显示,如下所示:

{
"_id":{"$oid":"5ea6a1f4d8d48409f9640319"},
"studentid":"A001",
"name":"Mohan Lal",
"subjects":["phy","che","maths"]
}

DictField

DictField 类的一个对象存储一个 Python 字典对象。在相应的数据库字段里,它也将被存储。

在上述范例中,将 ListField 类型改为 DictField 类型。

from mongoengine import *
connect('studentDB')
class Student(Document):
   studentid = StringField(required=True)
   name = StringField(max_length=50)
   subjects = DictField()
s1=Student()
s1.studentid='A001'
s1.name='Mohan Lal'
s1.subjects['phy']=60
s1.subjects['che']=70
s1.subjects['maths']=80
s1.save()

数据库中的文档表现如下−

{
"_id":{"$oid":"5ea6cfbe1788374c81ccaacb"},
"studentid":"A001",
"name":"Mohan Lal",
"subjects":{"phy":{"$numberInt":"60"},
            "che":{"$numberInt":"70"},
            "maths":{"$numberInt":"80"}
            }
}

ReferenceField

MongoDB 文档可以使用这种类型的字段存储对另一个文档的引用。借此,我们可以实现与 RDBMS 中的联接。ReferenceField 构造函数使用其他文档类的名称作为参数。

class doc1(Document):
   field1=StringField()
class doc2(Document):
   field1=StringField()
   field2=ReferenceField(doc1)

在下面的范例中,StudentDB 数据库包含两个文档类,即 student 和 teacher。Student 类的文档包含对 teacher 类的对象的引用。

from mongoengine import *
connect('studentDB')
class Teacher (Document):
   tid=StringField(required=True)
   name=StringField()
class Student(Document):
   sid = StringField(required=True)
   name = StringField()
   tid=ReferenceField(Teacher)

t1=Teacher()
t1.tid='T1'
t1.name='Murthy'
t1.save()

s1=Student()
s1.sid='S1'
s1.name='Mohan'
s1.tid=t1
s1.save()

运行上述代码,并在 Compass GUI 中确认结果。StudentDB 数据库中创建了两个与两个文档类相对应的集合。

添加的 teacher 文档如下:

{
"_id":{"$oid":"5ead627463976ea5159f3081"},
"tid":"T1",
"name":"Murthy"
}

student 文档的内容显示如下:

{
"_id":{"$oid":"5ead627463976ea5159f3082"},
"sid":"S1",
"name":"Mohan",
"tid":{"$oid":"5ead627463976ea5159f3081"}
}

请注意,Student 文档中的 ReferenceField 存储了相应 Teacher 文档的 _id。访问时,Student 对象会自动转换为一个引用,并且在访问相应的 Teacher 对象时取消引用。

如需向正在定义的文档添加引用,请使用 'self' 而不是其他文档类作为引用字段的参数。请注意,使用引用字段在获取文档方面可能会导致性能不佳。

ReferenceField 构造函数还具有一个可选参数 reverse_delete_rule。它的值决定了在引用的文档被删除时要执行的操作。

可能的值如下:

  1. DO_NOTHING (0) - 什么也不做(默认)。

  2. NULLIFY (1) - 将引用更新为 null。

  3. CASCADE (2) - 删除与引用关联的文档。

  4. DENY (3) - 阻止删除引用对象。

  5. PULL (4) - 从引用列表字段中提取引用

可以使用引用列表来实现一对多的关系。假设一个 student 文档不得不与一个或多个 teacher 文档关联,那么 Student 类必须具有 ReferenceField 实例的 ListField。

from mongoengine import *
connect('studentDB')
class Teacher (Document):
tid=StringField(required=True)
name=StringField()
class Student(Document):
   sid = StringField(required=True)
   name = StringField()
   tid=ListField(ReferenceField(Teacher))
t1=Teacher()
t1.tid='T1'
t1.name='Murthy'
t1.save()
t2=Teacher()
t2.tid='T2'
t2.name='Saxena'
t2.save()
s1=Student()
s1.sid='S1'
s1.name='Mohan'
s1.tid=[t1,t2]
s1.save()

在 Compass 中确认上述代码的结果时,您会发现 student 文档具有两个 teacher 文档的引用:

Teacher Collection
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e4"},
"tid":"T1","name":"Murthy"
}
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e5"},
"tid":"T2","name":"Saxena"
}
Student collection
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e6"},
"sid":"S1","name":"Mohan",
"tid":[{"$oid":"5eaebcb61ae527e0db6d15e4"},{"$oid":"5eaebcb61ae527e0db6d15e5"}]
}

DateTimeField

DateTimeField 类的实例允许在 MongoDB 数据库中以日期格式数据。MongoEngine 查找 Python-DateUtil 库以用适当的日期格式解析数据。如果在当前安装中没有找到它,则日期使用内置的 time 模块的 time.strptime() 功能表示。这种类型的字段的默认值是当前日期时间实例。

DynamicField

此字段可处理不同类型的各种数据。此类型的字段由 DynamicDocument 类内部使用。

ImageField

此类型的字段对应于文档中的字段,该字段可存储图像文件。此类的构造函数可以接受 size 和 thumbnail_size 参数(二者均以像素大小计)。

MongoEngine - Add/Delete Document

我们已使用 Document 类的 save() 方法在集合中添加了一个文档。还可以使用以下参数进一步自定义 save() 方法:

force_insert

默认为 False,如果设置为 True,则不允许更新现有文档。

validate

验证文档;设置为 False 以跳过。

clean

调用文档清理方法,验证参数应为 True。

write_concern

将用作执行 getLastError 命令所产生的选项。例如,save(…​, write_concern={w: 2, fsync: True}, …​) 将会等到至少两个服务器记录了写入,并且会强制对主服务器进行 fsync。

cascade

设置级联保存的标志。您可以通过在文档中设置 meta 中的“级联”来设置默认值。

cascade_kwargs

可传递给级联保存的可选关键字参数。相当于级联= True。

_refs

在级联保存中使用的已处理引用的列表

save_condition

仅在数据库中的匹配记录满足条件时执行保存。如果不满足条件,则引发 OperationError。

signal_kwargs

要传递给信号调用的 kwargs 字典。

在调用保存之前,您可以设置用于文档验证的清理规则。通过提供自定义 clean() 方法,您可以执行任何预验证/数据清理。

class MyDocument(Document):
   ...
   ...

   def clean(self):
      if <condition>==True:
         msg = 'error message.'
         raise ValidationError(msg)

请注意,只有在启用验证时才会调用 Cleaning,并且在调用 save() 时也会调用。

Document 类还具有 insert() 方法来执行批量插入。它具有以下参数:

doc_or_docs

待插入的文档或文档列表

load_bulk

如果为 True,则返回文档实例列表

write_concern

额外的关键字参数传递到 insert(),它将用作生成 getLastError 命令的选项。

signal_kwargs

(可选) 将传递给信号调用的 kwargs 词典

如果文档包含任何 ReferenceField 对象,则默认情况下 save() 方法不会保存对这些对象的任何更改。如果您希望保存所有引用,而不会注意到每个 save 都是一个单独的查询,那么将 cascade 作为 True 传递给 save 方法将级联任何保存。

通过调用 delete() 方法从集合中删除文档非常容易。请记住,只有在之前已保存文档时此操作才会生效。delete() 方法具有以下参数:

signal_kwargs

(可选) 将传递给信号调用的 kwargs 词典。

write_concern

传递的额外关键字参数将用作生成 getLastError 命令的选项。

要从数据库中删除整个集合,请使用 drop_collecction() 方法。它从数据库中删除与此 Document 类型关联的整个集合。如果未设置文档集合(例如,如果它是抽象的),则该方法会引发 OperationError。

文档类中的 modify() 方法在数据库中执行文档的原子更新并重新加载其更新版本。如果文档已更新,则返回 True;如果数据库中的文档与查询不匹配,则返回 False。请注意,如果该方法返回 True,则对该文档所做的所有尚未保存的更改将被拒绝。

Parameters

query

只有当数据库中的文档与查询匹配时,才会执行更新

update

Django-style update keyword arguments

MongoEngine - Querying Database

connect() 函数返回 MongoClient 对象。使用此对象提供的 list_database_names() 方法,我们可以检索服务器上的数据库数。

from mongoengine import *
con=connect('newdb')
dbs=con.list_database_names()
for db in dbs:
print (db)

还可以使用 list_collection_names() 方法获取数据库中的集合列表。

collections=con['newdb'].list_collection_names()
for collection in collections:
   print (collection)

如前所述,Document 类具有 objects 属性,该属性允许访问与数据库相关的对象。

newdb 数据库有一个与下面的 Document 类相对应的 products 集合。若要获取所有文档,我们按如下所示使用 objects 属性:

from mongoengine import *
con=connect('newdb')
class products (Document):
ProductID=IntField(required=True)
Name=StringField()
price=IntField()
for product in products.objects:
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

Output

ID: 1 Name: Laptop Price: 25000
ID: 2 Name: TV Price: 50000
ID: 3 Name: Router Price: 2000
ID: 4 Name: Scanner Price: 5000
ID: 5 Name: Printer Price: 12500

MongoEngine - Filters

objects 属性是一个 QuerySet 管理器。访问时,它会创建并返回一个 QuerySet。借助于字段名称作为关键字参数,可以对查询进行过滤。例如,从上面的 products 集合中,若要打印产品名为“TV”的文档的详细信息,我们使用 Name 作为关键字参数。

for product in products.objects(Name='TV'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

可以将 QuerySet 对象的 filter 方法用于将过滤器应用于查询。以下代码片段也返回了名为“TV”的产品详细信息。

qset=products.objects
for product in qset.filter(Name='TV'):
   print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

MongoEngine - Query Operators

除了使用 = 操作符检查是否相等外,MongoEngine 中还定义了以下逻辑操作符。

ne

not equal to

lt

less than

lte

小于或等于

gt

greater than

gte

大于或等于

not

否定标准检查,可以在其他操作符之前使用

in

value is in list

nin

值不在列表中

mod

value % x == y,其中 x 和 y 是提供的两个值

all

提供的列表中的每一项都在数组中

size

数组的大小是

exists

value for field exists

这些运算符必须附加到带有双下划线 __ 的字段名。

要使用大于运算符 (gt),使用以下格式 -

#using greater than operator
for product in products.objects(price__gt=10000):
   print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

Output

ID: 1 Name: Laptop Price: 25000
ID: 2 Name: TV Price: 50000
ID: 5 Name: Printer Price: 12500

in 运算符类似于 Python 的 in 运算符。对于与列表中名称相匹配的产品名称,使用以下代码 -

for product in products.objects(Name__in=['TV', 'Printer']):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

Output

ID: 2 Name: TV Price: 50000
ID: 5 Name: Printer Price: 12500

您可以使用以下运算符作为应用于查询的正则表达式的快捷方式 -

exact

字符串字段与值完全匹配

iexact

字符串字段与值完全匹配(不区分大小写)

contains

string field contains value

icontains

字符串字段包含值(不区分大小写)

startswith

字符串字段以值开头

istartswith

字符串字段以值开头(不区分大小写)

endswith

字符串字段以值结尾

iendswith

字符串字段以值结尾(不区分大小写)

match

执行 $elemMatch,以便可以在数组中匹配整个文档

例如,以下代码将打印名称中包含 'o' 的产品详细信息 -

for product in products.objects(Name__contains='o'):
   print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

Output

ID: 1 Name: Laptop Price: 25000
ID: 3 Name: Router Price: 2000

在另一个字符串查询示例中,以下代码显示以 'er' 结尾的名称 -

for product in products.objects(Name__endswith='er'):
   print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

Output

ID: 3 Name: Router Price: 2000
ID: 4 Name: Scanner Price: 5000
ID: 5 Name: Printer Price: 12500

MongoEngine - QuerySet Methods

QuerySet 对象拥有以下用于查询数据库的有用方法。

first()

将返回满足查询的第一个文档。以下代码将返回 products 集合中的第一个文档,价格 < 20000。

qset=products.objects(price__lt=20000)
doc=qset.first()
print ('Name:',doc.Name, 'Price:',doc.price)

Output

Name: Router Price: 2000

exclude()

这将导致从查询集中排除提到的字段。此处,Document 类的 to_json() 方法用于获取 Document 的 JSON 化版本。ProductID 字段不会出现在结果中。

for product in products.objects.exclude('ProductID'):
   print (product.to_json())

Output

{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "Name": "Laptop", "price": 25000}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "Name": "TV", "price": 50000}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "Name": "Router", "price": 2000}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "Name": "Scanner", "price": 5000}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "Name": "Printer", "price": 12500}

fields()

使用此方法控制加载到查询集中的字段。使用字段名作为关键字参数,并设置为 1 以包含,设置为 0 以排除。

for product in products.objects.fields(ProductID=1,price=1):
print (product.to_json())

Output

{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "ProductID": 1, "price": 25000}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "ProductID": 2, "price": 50000}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "ProductID": 3, "price": 2000}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "ProductID": 4, "price": 5000}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "ProductID": 5, "price": 12500}

fields() 方法中将字段关键字参数设置为 0 的效果类似于 exclude() 方法。

for product in products.objects.fields(price=0):
print (product.to_json())

Output

{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "ProductID": 1, "Name": "Laptop"}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "ProductID": 2, "Name": "TV"}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "ProductID": 3, "Name": "Router"}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "ProductID": 4, "Name": "Scanner"}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "ProductID": 5, "Name": "Printer"}

only()

这种方法的效果类似于 fields() 方法。只有与关键字参数对应的字段才会出现在查询集中。

for product in products.objects.only('Name'):
print (product.to_json())

Output

{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "Name": "Laptop"}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "Name": "TV"}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "Name": "Router"}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "Name": "Scanner"}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "Name": "Printer"}

sum()

此方法计算查询集中给定字段的总和。

average()

此方法计算查询集中给定字段的平均值。

avg=products.objects.average('price')
ttl=products.objects.sum('price')
print ('sum of price field',ttl)
print ('average of price field',avg)

Output

sum of price field 94500
average of price field 18900.0

MongoEngine - Sorting

QuerySet 的 order_by() 函数用于按顺序获取查询结果。用法如下:

Qset.order_by(‘fieldname’)

默认情况下,排序顺序为升序。对于降序,将 - 号附加到字段名称。例如,按升序获取价格列表:

from mongoengine import *
con=connect('newdb')

class products (Document):
   ProductID=IntField(required=True)
   company=StringField()
   Name=StringField()
   price=IntField()
for product in products.objects.order_by('price'):
   print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))

Output

Name:Router company:Iball price:2000
Name:Scanner company:Cannon price:5000
Name:Printer company:Cannon price:12500
Name:Laptop company:Acer price:25000
Name:TV company:Philips price:31000
Name:Laptop company:Dell price:45000
Name:TV company:Samsung price:50000

以下代码将按 name 降序获取列表:

for product in products.objects.order_by('-Name'):
   print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))

Output

Name:TV company:Samsung price:50000
Name:TV company:Philips price:31000
Name:Scanner company:Cannon price:5000
Name:Router company:Iball price:2000
Name:Printer company:Cannon price:12500
Name:Laptop company:Acer price:25000
Name:Laptop company:Dell price:45000

您还可以在多个字段上进行排序。此代码将按升序获取按公司、价格列表。

for product in products.objects.order_by('company','price'):
   print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))

Output

Name:Laptop company:Acer price:25000
Name:Scanner company:Cannon price:5000
Name:Printer company:Cannon price:12500
Name:Laptop company:Dell price:45000
Name:Router company:Iball price:2000
Name:TV company:Philips price:31000
Name:TV company:Samsung price:50000

MongoEngine - Custom Query Sets

默认情况下,文档类上的 object 属性返回未应用任何筛选器的 QuerySet。但是,您可以在文档上定义类方法来修改查询集。此类方法应该接受两个参数 - doc_cls 和 queryset ,并且需要用 queryset_manager() 装饰它才能被识别。

@queryset_manager
   def qry_method(docs_cls,queryset):
       ….
       ----

在下面的示例中,名为 products 的文档类有一个经过 @queryset_manager 装饰的 expensive_prods() 方法。该方法本身将过滤器应用于查询集,以便仅返回价格 >20000 的对象。此方法现在是默认文档查询,products 类的 objects 属性返回已筛选的文档。

from mongoengine import *
con=connect('newdb')

class products (Document):
   ProductID=IntField(required=True)
   company=StringField()
   Name=StringField()
   price=IntField()

   @queryset_manager
   def expensive_prods(docs_cls,queryset):
      return queryset.filter(price__gt=20000)
for product in products.expensive_prods():
   print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))

Output

Name:Laptop company:Acer price:25000
Name:TV company:Samsung price:50000
Name:TV company:Philips price:31000
Name:Laptop company:Dell price:45000

如果您希望自定义过滤文档的方法,请首先声明 QuerySet 类的子类,并将其用作元数据中的 queryset_class 属性的值。

以下示例使用 MyQuerySet 类作为自定义查询集的定义。此类中的 myqrymethod() 过滤名称字段以“er”结尾的文档。在 products 类中,meta 属性引用此查询集子类,用作 queryset_class 属性的值。

from mongoengine import *
con=connect('newdb')
class MyQuerySet(QuerySet):
   def myqrymethod(self):
      return self.filter(Name__endswith='er')
class products (Document):
   meta = {'queryset_class': MyQuerySet}
   ProductID=IntField(required=True)
   company=StringField()
   Name=StringField()
   price=IntField()
for product in products.objects.myqrymethod():
   print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))

Output

Name:Router company:Iball price:2000
Name:Scanner company:Cannon price:5000
Name:Printer company:Cannon price:12500

MongoEngine - Indexes

索引的集合导致更快的查询处理。默认情况下,每个集合都自动在 _id 字段上建立索引。此外,您可以在一个或多个字段上创建索引。

使用 Compass,我们可以非常轻松地构建索引。单击下图所示的 Indexes 选项卡上的 CREATE INDEX 按钮:

indexes

将出现一个如所示的对话框。选择索引的名称、要建立索引的字段、索引的顺序(升序或降序)和其他选项。

create indexes

在使用 MongoEngine 时,索引是通过指定 Document 类定义的元数据中的“indexes”键创建的。

indexes 属性的值是字段列表。在以下示例中,我们要求 student 集合中的文档按 name 字段建立索引。

from mongoengine import *
con=connect('mydata')
class student(Document):
   name=StringField(required=True)
   course=StringField()
   meta = {'indexes':['name']}

s1=student()
s1.name='Avinash'
s1.course='DataScience'
s1.save()
s2=student()
s2.name='Anita'
s2.course='WebDesign'
s2.save()

默认情况下,索引顺序为升序。可以通过对升序添加“+”,对降序添加“-”来指定顺序。

要创建复合索引,请使用字段名称的元组,可以选择附加 + 或 - 符号来指示排序顺序。

在以下示例中,student 文档类包含对名称和课程的复合索引的定义(注意 - 以课程字段为前缀的符号,表示索引按名称升序和课程降序构建。

from mongoengine import *
con=connect('mydata')

class student(Document):
   name=StringField(required=True)
   course=StringField()
   meta = {'indexes':[('name','-course')]}

s1=student()
s1.name='Avinash'
s1.course='DataScience'
s1.save()
s2=student()
s2.name='Anita'
s2.course='WebDesign'
s2.save()

MongoDB Compass 将如下所示显示索引 -

value of indexes

“索引”的值可能是各种选项的字典,如下所示:

fields

The fields to index.

cls

如果允许继承已启用,则可以配置是否应自动添加 _cls 字段到索引。

sparse

索引是否应为稀疏的。

unique

索引是否应是唯一的。

expireAfterSeconds

从集合中自动使数据过期,方式是设置时间(以秒为单位)

name

允许为索引指定名称

collation

允许创建不区分大小写的索引

以下示例在 name 字段上创建索引,该索引在 3600 秒后过期。

from mongoengine import *
con=connect('mydata')

class student(Document):
   name=StringField(required=True)
   course=StringField()
   meta = {'indexes':[{
            'fields': ['name'],
            'expireAfterSeconds': 3600
           }
    ]
}

要指定文本索引,请使用“$”符号作为字段名称前缀,对于哈希索引,请使用“#”作前缀。

如此指定的索引在向集合中添加文档时会自动创建。要禁用自动创建,请在元属性中将 “ auto_create_index ” 设置为 False。

我们有带有 Document 类的方法 list_indexes() ,它显示可用索引的列表。

print (student.list_indexes())

[[('name', 1)], [('_id', 1)]]

要在元字典中不存在的字段上创建索引,请使用 create_index() 方法。以下代码将在 course 字段上创建索引 -

class student(Document):
name=StringField(required=True)
course=StringField()
meta = {'indexes':[{
          'fields': ['name'],
          'expireAfterSeconds': 3600
        }
]}
student.create_index(['course'])

MongoEngine - Aggregation

术语 “ aggregation ” 用于对数据进行处理并返回计算结果的操作。对集合中一个或多个文档字段求和、计数和平均值可称为聚合函数。

MongoEngine 提供了 aggregate() 函数,该函数封装了 PyMongo 的聚合框架。聚合操作使用集合作为输入,并返回一个或多个文档作为结果。

MongoDB 使用数据处理管道这一概念。一个 pipeline 可以有多个阶段。基本阶段提供了提供过滤器和像查询一样运作的内容。其他阶段提供了按一个或多个字段进行分组和/或排序、字符串连接任务、数组聚合工具等的工具。

在 MongoDB 管道创建中定义了以下阶段 -

Name

Description

$project

通过添加新字段或删除现有字段来调整流中每个文档的形状。

$match

对文档流进行筛选,仅允许匹配的文档未经修改进入下一阶段。$match 使用标准 MongoDB 查询。

$redact

通过基于文档中存储的信息限制每条文档的内容,以重新调整每条文档的形状。

$limit

限制文档未经修改地传递给管道

$skip

跳过前 n 个文档,并将剩下的文档未经修改地传递到管道中。

$group

按给定的标识符表达式对输入文档进行分组,并将累加器表达式应用于每个组。输出文档仅包含标识符字段和累加字段。

$sort

按指定的排序键重新排序文档流。

$out

将聚合管道的输出文档写入到集合中。

聚合表达式使用字段路径来访问输入文档中的字段。要指定字段路径,请在字段名称前添加一个美元符号 $。表达式可以使用一个或多个布尔运算符($and、$or、$not)和比较运算符($eq、$gt、$lt、$gte、$lte 和 $ne)。

以下算术表达式也用于聚合 −

$add

相加数字返回总和。接受任意数量的参数表达式

$subtract

返回将第二个值从第一个值中减去的结果

$multiply

相乘数字返回乘积。接受任意数量的参数表达式

$divide

返回将第一个数除以第二个数的结果。接受两个参数表达式

$mod

返回第一个数字除以第二个数字的余数。接受两个参数表达式

以下字符串表达式也可用于聚合中 −

$concat

连接任意数量的字符串

$substr

从指定索引位置开始、最多到指定长度,返回字符串的子字符串

$toLower

将字符串转换为小写。接受一个参数表达式

$toUpper

将字符串转换为大写。接受一个参数表达式

$strcasecmp

执行字符串比较,如果两个字符串相等则返回 0,如果第一个字符串大于第二个字符串则返回 1,如果第一个字符串小于第二个字符串则返回 -1

为了演示 aggregate() 函数在 MongoEngine 中的工作方式,我们首先来定义一个名为 orders 的文档类。

from mongoengine import *
con=connect('mydata')

class orders(Document):
   custID = StringField()
   amount= IntField()
   status = StringField()

然后,我们在 orders 集合中添加以下文档 −

_id

custID

amount

status

ObjectId("5eba52d975fa1e26d4ec01d0")

A123

500

A

ObjectId("5eba536775fa1e26d4ec01d1")

A123

250

A

ObjectId("5eba53b575fa1e26d4ec01d2")

B212

200

D

ObjectId("5eba540e75fa1e26d4ec01d3")

B212

400

A

aggregate() 函数用于查找仅当 status 等于 'A' 时每个 custID 的 amount 字段的和。于是,管道建立如下。

管道中的第一阶段使用 $match 来筛选 status='A' 的文档。第二阶段使用 $group 标识符对 CustID 上的文档进行分组并对 amount 求和。

 pipeline = [
{"$match" : {"status" : "A"}},
{"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
]

此管道现在用作 aggregate() 函数的参数。

docs = orders.objects().aggregate(pipeline)

我们可以使用 for 循环对文档游标进行迭代。完整的代码如下所示 −

from mongoengine import *
con=connect('mydata')

class orders(Document):
   custID = StringField()
   amount= IntField()
   status = StringField()

pipeline = [
   {"$match" : {"status" : "A"}},
   {"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
   ]
docs = orders.objects().aggregate(pipeline)
for doc in docs:
   print (x)

对于给定的数据,将生成以下输出 −

{'_id': 'B212', 'total': 400}
{'_id': 'A123', 'total': 750}

MongoEngine - Advanced Queries

为了提高在文档中检索字段子集的效率,请使用 Objects 属性的 only() 方法。这将显著提高性能,特别是对于极大长度的字段,例如 ListField。将所需的字段传递给 only() 函数。如果在执行 only() 查询后访问了其他字段,则返回默认值。

from mongoengine import *
con=connect('newdb')
class person (Document):
name=StringField(required=True)
city=StringField(default='Mumbai')
pin=IntField()
p1=person(name='Himanshu', city='Delhi', pin=110012).save()
doc=person.objects.only('name').first()
print ('name:',doc.name)
print ('city:', doc.city)
print ('PIN:', doc.pin)

Output

name: Himanshu
city: Mumbai
PIN: None

Note − 使用 city 属性的值作为默认值。由于未为 PIN 指定默认值,因此打印 None。

如果您需要丢失的字段,可以调用 reload() 函数。

当文档类具有 ListField 或 DictField 时,在对其进行迭代时,任何 DBREf 对象都会自动取消引用。为了进一步提高效率,特别是如果文档具有 ReferenceField,可以使用 select_related() 函数对查询数量进行限制,该函数会将 QuerySet 转换为列表并取消引用。

MongoEngine API 包含 Q 类,该类可用于构建由多个约束条件组成的复杂查询。Q 表示查询的一部分,该部分可以通过关键字参数语法和二进制 & 和 | 运算符来初始化。

person.objects(Q(name__startswith=’H’) &Q(city=’Mumbai’))

MongoEngine - Document Inheritance

可以为任何用户定义的文档类定义一个派生类。派生类可能会根据需要添加其他字段。但是,由于此类不是 Document 类的直接子类,因此它不会创建新集合,而是将其对象存储在父类使用的集合中。在父类中,meta 属性 ' allow_inheritance 在以下示例中,我们首先将 employee 定义为文档类,并将 allow_inheritance 设置为 true。salary 类是从 employee 派生的,它添加了另外两个字段 dept 和 sal。Employee 及 salary 类的对象存储在 employee 集合中。

在以下示例中,我们首先将 employee 定义为文档类,并将 allow_inheritance 设置为 true。salary 类是从 employee 派生的,它添加了另外两个字段 dept 和 sal。Employee 及 salary 类的对象存储在 employee 集合中。

from mongoengine import *
con=connect('newdb')
class employee (Document):
name=StringField(required=True)
branch=StringField()
meta={'allow_inheritance':True}
class salary(employee):
dept=StringField()
sal=IntField()
e1=employee(name='Bharat', branch='Chennai').save()
s1=salary(name='Deep', branch='Hyderabad', dept='Accounts', sal=25000).save()

我们可以验证 employee 集合中存储了两个文档,如下所示 −

{
"_id":{"$oid":"5ebc34f44baa3752530b278a"},
"_cls":"employee",
"name":"Bharat",
"branch":"Chennai"
}
{
"_id":{"$oid":"5ebc34f44baa3752530b278b"},
"_cls":"employee.salary",
"name":"Deep",
"branch":"Hyderabad",
"dept":"Accounts",
"sal":{"$numberInt":"25000"}
}

请注意,为了识别相应的文档类,MongoEngine 添加了一个 “_cls” 字段,并将它的值设置为 “employee” 和 “employee.salary”。

如果您想为一群文档类提供额外的功能,但没有继承的开销,那么您可以首先创建一个 abstract 类,然后从同一类派生一个或多个类。要使类抽象,将 meta 属性 'abstract' 设置为 True。

from mongoengine import *
con=connect('newdb')

class shape (Document):
   meta={'abstract':True}
   def area(self):
      pass

class rectangle(shape):
   width=IntField()
   height=IntField()
   def area(self):
      return self.width*self.height

r1=rectangle(width=20, height=30).save()

MongoEngine - Atomic Updates

原子性是 ACID 事务属性之一。数据库事务必须是不可分割和不可简化的,以便要么完全发生,要么根本不发生。此属性称为原子性。MongoDB 仅对单一文档支持原子性,而不是多文档事务。

MongoEngine 为查询集的原子更新提供了以下方法。

update_one() - 覆盖或添加与查询匹配的第一条文档。

update() - 对与查询匹配的字段执行原子更新。

modify() - 更新文档并将其返回。

以下的修改器可能会与这些方法一起使用。(这些修改器在字段之前,而不是之后)。

set

set a particular value

unset

delete a particular value

inc

按给定量增加值

dec

按给定量减少值

push

将值追加到列表

push_all

将多个值追加到列表

pop

根据值删除列表第一个或最后一个元素

pull

从列表中删除一个值

pull_all

从列表中删除多个值

add_to_set

仅在列表中不存在某个值时,才将该值添加到列表

以下是一个原子更新的示例,我们首先创建一个名为测试的文档类,并在其中添加一个文档。

from mongoengine import *
con=connect('newdb')

class tests (Document):
   name=StringField()
   attempts=IntField()
   scores=ListField(IntField())

t1=tests()
t1.name='XYZ'
t1.attempts=0
t1.scores=[]
t1.save()

让我们使用 update_one() 方法将 name 字段从 XYZ 更新为 MongoDB。

tests.objects(name='XYZ').update_one(set__name='MongoDB')

push 修改器用于在 ListField(scores)中添加数据。

tests.objects(name='MongoDB').update_one(push__scores=50)

若要将 attempts 字段增加 1,我们可以使用 inc 修改器。

tests.objects(name='MongoDB').update_one(inc__attempts=1)

更新后的文档如下所示 -

{
"_id":{"$oid":"5ebcf8d353a48858e01ced04"},
"name":"MongoDB",
"attempts":{"$numberInt":"1"},
"scores":[{"$numberInt":"50"}]
}

MongoEngine - Javascript

MongoEngine 的 QuerySet 对象具有 exec_js() 方法,该方法允许在 MongoDB 服务器上执行 Javascript 函数。此函数处理以下参数 -

exec_js(code, *field_names, **options)

其中,

  1. code - 一个包含要执行的 Javascript 代码的字符串

  2. fields - 在函数中使用,作为参数传递

  3. options - 您希望函数中可用的选项(在 Javascript 中通过 options 对象进行访问)

此外,还可以按以下方式将一些其他变量提供给函数作用域:

  1. collection - 与 Document 类对应的集合的名称。应该将该名称用于在 Javascript 代码中从 db 获取 Collection 对象。

  2. query - 由 QuerySet 对象生成的查询;传递到 Javascript 函数中 Collection 对象的 find() 方法。

  3. options - 一个包含 exec_js() 中传递的关键字参数的对象。

请注意,MongoEngine 文档类中的属性在数据库中使用不同的名称(使用 Field 构造函数中的 db_field 关键字参数设置)。

class BlogPost(Document):
title = StringField(db_field='doctitle')

为此,存在一种机制,可以在 Javascript 代码中将 MongoEngine 字段属性替换为数据库字段名。

当在集合对象中访问一个字段时,请使用方括号符号,并在 MongoEngine 字段名前加上一个波形符号 (~)。波形符号后的字段名将转换为在数据库中使用的名称。

document': doc[~title];

请注意,当 Javascript 代码引用嵌入式文档中的字段时,应在嵌入式文档中的字段名之前使用嵌入式文档字段的名称,后跟一个点。

MongoEngine - GridFS

在 MongoDB 中,使用 GridFS 规范存储大小超过 16 MB 的文件。一个文件被分成多个块,每个块的默认大小为 255KB。大块可以尽可能大。GridFS 使用两个集合,一个用于块,另一个用于元数据。

如果您想访问 GridFS 存储的任何文件,而不必将整个文件加载到内存中,可以使用 GridFS。

MongoEngine API 通过 FileField 对象支持 GridFS。使用此对象,可以插入和检索数据。FileField 对象的 put() 方法有助于将文件写为 Document 的一部分。

from mongoengine import *
con=connect('newdb')

class lang (Document):
   name=StringField()
   developer=StringField()
   logo=FileField()

l1=lang()
l1.name='Python'
l1.developer='Van Rossum'
f=open('pylogo.png','rb')
l1.logo.put(f,content_type='image/png')
l1.save()

可以使用 Python 文件对象中的 read() 方法检索 FileField 的内容。

logo = l1.logo.read()

另外还有 delete() 方法,用于删除存储的文件。

l1 = lang.objects(name='Python').first()
l1.logo.delete()
l1.save()

请注意,FileField 仅将文件 ID 存储在单独的 GridFS 集合中。因此 delete() 方法不会实际删除文件。

replace() 方法有助于将文件引用替换为另一个文件。

l1 = lang.objects(name='Python').first()
f=open('newlogo.png','rb')
l1.logo.replace(f,content_type='image/png')
l1.save()

MongoEngine - Signals

信号是由发送方对象发送的事件,任意数量的接收方对象可以订阅此类事件。信号接收方可以订阅特定的发送方,或者可以接收来自多个发送方的信号。

在 MongoEngine 中,信号处理由 blinker 库支持,这意味着你需要使用 pip 实用程序来安装它。mongoengine.signals 模块定义了以下信号 −

pre_init

在创建新的 Document 或 EmbeddedDocument 实例时调用,并在收集构造函数参数但尚未对其进行任何其他处理后执行。

post_init

在所有 Document 或 EmbeddedDocument 实例的处理完成后调用。

pre_save

在执行任何操作之前在 save() 中调用。

pre_save_post_validation

在 save() 中进行验证后但在保存之前调用。

post_save

在大多数操作(验证,插入/更新)成功完成后在 save() 中调用。传递一个附加的布尔关键字参数,以指明保存是插入还是更新。

pre_delete

在尝试执行删除操作之前在 delete() 中调用。

post_delete

在成功删除记录时在 delete() 中调用。

pre_bulk_insert

在验证要插入的文档后但在写入任何数据之前调用。

post_bulk_insert

在成功批量插入操作后调用。一个附加的布尔参数 loaded 将文档的内容标识为当 True 为 Document 实例或当 False 为已插入记录的主键值列表。

然后将事件处理程序函数附加到 Document 类。请注意, EmbeddedDocument 仅支持 pre/post_init 信号。pre/post_save 等只能附加到 Document 类。

你还可以使用装饰器快速创建多个信号并将它们作为类装饰器附加到 Document 或 EmbeddedDocument 子类。

在以下示例中,用作信号处理程序的演示,我们还使用 Python 的标准库模块——记录并设置记录级别以进行调试。

from mongoengine import *
from mongoengine import signals
import logging
logging.basicConfig(level=logging.DEBUG)

然后,我们编写一个文档类,以便在 newdb 数据库中创建相应的集合。在类中,定义了两个类的方法 pre_save() 和 post_save(),目的是在将文档保存在 Author 集合中之前和之后调用。

class Author(Document):
   name = StringField()

   def __unicode__(self):
      return self.name

   @classmethod
   def pre_save(cls, sender, document, **kwargs):
      logging.debug("Pre Save: %s" % document.name)

   @classmethod
   def post_save(cls, sender, document, **kwargs):
      logging.debug("Post Save: %s" % document.name)
      if 'created' in kwargs:
         if kwargs['created']:
            logging.debug("Created")
         else:
            logging.debug("Updated")

这两个类方法都被定义为带有 classname、发送者对象和带有可选关键字参数列表的文档的参数。

最后,我们注册信号处理程序。

signals.pre_save.connect(Author.pre_save, sender=Author)
signals.post_save.connect(Author.post_save, sender=Author)

当我们创建 Document 子类的实例时,控制台日志将显示由相应事件处理程序处理的 pre 和 post 保存信号。

Author(name="Lathkar").save()

Python 控制台按如下所示报告日志 −

DEBUG:root:Pre Save: Lathkar
DEBUG:root:Post Save: Lathkar
DEBUG:root:Created

MongoDB 支持使用可以对字符串内容执行文本搜索的查询运算符。如前所述,要设置文本索引,请使用 $ 符号作为索引的前缀名。对于文本索引,索引字段的权重表示相对于其他索引字段在文本搜索分数方面的字段的重要程度。你还可以再类的元字典中指定默认语言。

支持的语言列表位于 https://docs.mongodb.com/manual/reference/text-search-languages/ 。MongoEngine API 由 QuerySet 对象的 search_text() 方法组成。在索引字段中要搜索的字符串作为参数给出。

在以下示例中,我们首先定义一个名为 lang 的 Document 类,其中包含两个字符串字段,即语言的名称及其特征。我们还在两个字段上分别创建了具有相应权重的索引。

from mongoengine import *
con=connect('newdb')

class lang (Document):
   name=StringField()
   features=StringField()
   meta = {'indexes': [
      {'fields': ['$name', "$features"],
         'default_language': 'english',
         'weights': {'name': 2, 'features': 10}
      }]
   }

l1=lang()
l1.name='C++'
l1.features='Object oriented language for OS development'
l1.save()
l2=lang()
l2.name='Python'
l2.features='dynamically typed and object oriented for data science, AI and ML'
l2.save()
l3=lang()
l3.name='HTML'
l3.features='scripting language for web page development'
l3.save()

为了对单词“oriented”执行搜索,我们使用 search_text() 方法,如下所示:

docs=lang.objects.search_text('oriented')
for doc in docs:
   print (doc.name)

上述代码的输出是其描述中包含单词“oriented”(在本例中为“Python”和“C++”)的语言的名称。

MongoEngine - Extensions

MongoEngine 与以下库完美集成:

marshmallow_mongoengine

marshmallow 是一款与 ORM/ODM/框架无关的序列化/反序列化库,用于在复杂数据类型(例如对象)和原生 Python 数据类型之间进行转换。使用 MongoEngine 的此扩展,我们可以轻松地执行序列化/反序列化操作。

首先,像往常一样创建 Document 类,如下所示:

import mongoengine as me
class Book(me.Document):
title = me.StringField()

然后使用以下代码生成 marshmallow 模式:

from marshmallow_mongoengine import ModelSchema
class BookSchema(ModelSchema):
   class Meta:
      model = Book

b_s = BookSchema()

使用如下代码保存文档:

book = Book(title='MongoEngine Book').save()

并使用 dump(0 执行序列化/反序列化,并使用以下代码加载():

data = b_s.dump(book).data
b_s.load(data).data

Flask-MongoEngine

这是一个为 MongoEngine 提供集成的 Flask 扩展。该库可以轻松处理应用程序的 MongoDB 数据库连接管理。您还可以将 WTForms 作为模型的模型表单使用。

安装 flask-mongoengine 包后,使用以下设置初始化 Flask 应用程序:

from flask import Flask
from flask_mongoengine import MongoEngine

app = Flask(__name__)
app.config['MONGODB_SETTINGS'] = {
   'db': 'mydata',
   'host': 'localhost',
   'port':27017
}
db = MongoEngine(app)

然后使用以下代码定义 Document 子类:

class book(me.Document):
name=me.StringField(required=True)

声明上述类的对象,并在访问特定路由时调用 save() 方法。

@app.route('/')
def index():
b1=book(name='Introduction to MongoEngine')
b1.save()
return 'success'

extras-mongoengine

此扩展包含其他字段类型和任何其他奥术。

Eve-MongoEngine

Eve 是一个为人类设计的开源 Python REST API 框架。它允许轻松构建和部署高度可自定义且功能齐全的 RESTful Web 服务。

Eve 由 Flask 和 Cerberus 提供支持,并提供对 MongoDB 数据存储的本机支持。Eve-MongoEngine 为 Eve 提供了 MongoEngine 集成。

使用以下代码安装和导入该扩展:

import mongoengine
from eve import Eve
from eve_mongoengine import EveMongoengine

配置设置并初始化 Eve 实例。

my_settings = {
'MONGO_HOST': 'localhost',
'MONGO_PORT': 27017,
'MONGO_DBNAME': 'eve_db'
app = Eve(settings=my_settings)
# init extension
ext = EveMongoengine(app)

按所示定义一个文档类:

class Person(mongoengine.Document):
name = mongoengine.StringField()
age = mongoengine.IntField()

添加模型并运行应用程序,最后使用以下代码:

ext.add_model(Person)
app.run()

Django-MongoEngine

此扩展的目的是将 MongoEngine 与 Django API 集成在一起,这是一个非常流行的 Python Web 开发框架。此项目仍在开发中。