数据库
首页 > 数据库> > python使用ODM控制Mongodb(MongoEngine)

python使用ODM控制Mongodb(MongoEngine)

作者:互联网

1.安装

pip install mongoengine

2.连接数据库

要连接一个 mongod实例, 需要用到 connect() 函数。分不同情况需提供不同的连接参数。

2.1 默认情况,指mongod运行在localhost且端口为27017)

只需要提供需要连接的数据库名即可:

from mongoengine import connect
connect('project1')

2.2 mongod运行在其他服务器或者端口不为默认27107端口

from mongoengine import connect
connect('project1', host='192.168.1.35', port=12345)

2.3 需要用户登陆验证

from mongoengine import connect
connect('project1', username='webapp', password='pwd123', authentication_source='admin')

2.4 URI连接方式

将URI给host参数赋值

from mongoengine import connect
connect('project1', host='mongodb://localhost/database_name')

注意
如URI包含数据库名,用户名或者密码,将会覆盖相应参数的传值。如以下例子,连接的数据库将会是production,用户名为admin,密码为qwerty。

from mongoengine import connect
connect(
    db='test',
    username='user',
    password='12345',
    host='mongodb://admin:qwerty@localhost/production'
)

2.5 连接副本集

from mongoengine import connect

# Regular connect
connect('dbname', replicaset='rs-name')

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

2.6 多数据库连接

连接多个数据库可以使用 connect() 并设置别名参数alias,如没有设置别名,则默认为 “default” 。

connect(db='test01', alias='testdbA')
connect(db='test02', alias='testdbB')
connect(db='test03', alias='testdbC')

也可以使用 register_connection() 进行多个数据库连接信息的存储。

register_connection(
    alias="default",
    db='test'
    )

当单独Document类需要调用特定数据库时,只需要在meta的 db_alias 设置对应的数据库别名即可。

class User(Document):
    name = StringField()

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

class Book(Document):
    name = StringField()

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

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

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

3. 上下文管理

3.1 切换数据库

使用switch_db ,提供需要切换数据库的类与所需切换到的数据库别名即可进行切换数据库。

from mongoengine.context_managers import switch_db

class User(Document):
    name = StringField()

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

with switch_db(User, 'archive-user-db') as User:
    User(name='Ross').save()  # Saves the 'archive-user-db'

3.2 切换集合(数据表)

使用 switch_collection ,提供需要切换集合的类与所需切换到的集合名即可进行切换集合。

from mongoengine.context_managers import switch_collection

class Group(Document):
    name = StringField()

Group(name='test').save()  # Saves in the default db

with switch_collection(Group, 'group2000') as Group:
    Group(name='hello Group 2000 collection!').save()  # Saves in group2000 collection

注意

使用上下文管理之前,需使用 register_connection()connect() 登记需要使用到的数据库信息。

4. 定义文件

MongoEngine允许您为文档定义模式,因为这有助于减少编码错误,并允许在可能存在的字段上定义实用方法
下面来一个给一个例子,要为文档定义模式,请创建一个继承自其的类 Document。通过将字段对象作为类属性添加到文档类来指定字段

from mongoengine import *
import datetime

class Page(Document):
    title = StringField(max_length=200, required=True)
    date_modified = DateTimeField(default=datetime.datetime.utcnow)

5.字段

默认情况下,字段不是必需的。要使字段成为必填字段,请将字段的required关键字参数设置 为True。字段也可能有可用的验证约束(max_length例如上面的示例中)。字段也可以采用默认值,如果没有提供值,将使用默认值。默认值可以可选地是可调用的,将被调用来检索该值(例如在上面的示例中)。可用的字段类型如下所示

BinaryField 二进制数据字段 
BooleanField 
ComplexDateTimeField 
DateTimeField 
DecimalField 
DictField 
DynamicField 
EmailField 
EmbeddedDocumentField 
EmbeddedDocumentListField 
FileField GridFS存储字段 
FloatField 
GenericEmbeddedDocumentField 
GenericReferenceField 
GenericLazyReferenceField 
GeoPointField 
ImageField 图像文件存储区域 
IntField 
ListField 
MapField 
ObjectIdField 
ReferenceField 
LazyReferenceField 
SequenceField 
SortedListField 
StringField 
URLField 
UUIDField 
PointField 
LineStringField 
PolygonField 
MultiPointField 
MultiLineStringField 
MultiPolygonField

6.字段参数

每个字段类型可以通过关键字参数进行定制。以下关键字参数可以在所有字段上设置:

class ExampleFirst(Document):
    # 默认是一个空列表
    values = ListField(IntField(), default=list)

class ExampleSecond(Document):
    values = ListField(IntField(), default=lambda: [1,2,3])

class ExampleDangerous(Document):
    values = ListField(IntField(), default=[1,2,3])

#可以是值的嵌套元组(存储在mongo中)和人类可读的密钥
SIZE  =  (('S' , 'Small' ),
        ('M' , 'Medium' ),
        ('L' , 'Large' ),
        ('XL' , 'Extra Large' ),
        ('XXL' , '特大号' ))
class Shirt (Document ):
    size  =  StringField (max_length = 3 , choices = SIZE 

#或者是一个只包含值的平坦迭代器
SIZE  =  ('S' , 'M' , 'L' , 'XL' , 'XXL' )
class  Shirt (Document ):
    size  =  StringField (max_length = 3 , choices = SIZE )

7.列表字段

MongoDB允许存储项目列表。要想添加列表类型到 Document,请使用ListField字段类型。ListField将另一个字段对象作为其第一个参数,该参数指定列表中可以存储哪些类型元素

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

8. 嵌入式文档

MongoDB能够在其他文档中嵌入文档。可以为这些嵌入式文档定义概要,就像它们可能用于常规文档一样。要创建嵌入式文档,只需像往常一样定义一个文档,但继承EmbeddedDocument而不是 Document:

class Comment(EmbeddedDocument):
    content = StringField()

要将文档嵌入到另一个文档中,请使用 EmbeddedDocumentField字段类型,将嵌入的文档类作为第一个参数提供:

class  Page (Document ):
    comments  =  ListField (EmbeddedDocumentField (Comment ))

comment1  =  Comment (content = 'Good work!' )
comment2  =  Comment (content = 'Nice article!' )
page  =  Page (comments = [ comment1 , comment2 ])

9.字典字段

通常,可以使用嵌入式文档而不是字典 - 通常建议使用嵌入式文档,因为字典不支持验证或自定义字段类型。但是,有时你不会知道你想要存储的结构; 在这种情况下DictField是合适的:

class SurveyResponse(Document):
    date = DateTimeField()
    user = ReferenceField(User)
    answers = DictField()

survey_response = SurveyResponse(date=datetime.utcnow(), user=request.user)
response_form = ResponseForm(request.POST)
survey_response.answers = response_form.cleaned_data()
survey_response.save()

字典可以存储复杂的数据,其他字典,列表,对其他对象的引用,所以是最灵活的可用字段类型

10.引用字段

要使引用字段可以存储到数据库中的其他文档,可以使用 ReferenceField。传入另一个文档类作为构造函数的第一个参数,然后将文档对象分配给该字段

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

该User对象在幕后自动变为引用,并在Page检索对象时解除引用。

要添加一个ReferenceField引用正在定义的文档的引用,请使用该字符串’self’代替文档类作为ReferenceField构造函数的参数。要引用尚未定义的文档,请使用未定义文档的名称作为构造函数的参数

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

class ProfilePage(Document):
    content = StringField()

11.一对多与ListField

如果通过引用列表实现一对多关系,则引用将存储为DBRefs,并且需要将对象的实例传递给查询

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

# 查找所有由bob编写的网页,此处用到了高级查询,后面会介绍
Page.objects(authors__in=[bob])

# 查找所有由bob和john编写的网页
Page.objects(authors__all=[bob, john])

# 删除由bob编写的某篇文章(根据某个id查找到的).
Page.objects(id='...').update_one(pull__authors=bob)

# 把john添加到某篇文章(根据某个id查找到的).

    Page.objects(id='...').update_one(push__authors=john)

12.处理引用文档的删除

默认情况下,MongoDB不检查数据的完整性,因此删除其他文档仍然存在引用的文档将导致一致性问题。Mongoengine 的ReferenceField增加了一些功能来防范这些类型的数据库完整性问题,为每个引用提供删除规则规范。通过reverse_delete_rule在ReferenceField定义上提供属性 来指定删除规则,如下所示

class ProfilePage(Document):
    ...
    employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE)
#这个例子中的声明意味着当一个Employee对象被移除时,ProfilePage引用该雇员的那个也被移除。如果删除了整批员工,则所有链接的配置文件页面也会被删除

它的值可以采用以下任何常量:

13. 通用参考字段

第二种场景也存在, GenericReferenceField。这允许您引用任何类型的Document,因此不会将 Document子类作为构造函数参数:

class Link(Document):
    url = StringField()

class Post(Document):
    title = StringField()

class Bookmark(Document):
    bookmark_object = GenericReferenceField()

link = Link(url='http://hmarr.com/mongoengine/')
link.save()

post = Post(title='Using MongoEngine')
post.save()

Bookmark(bookmark_object=link).save()
Bookmark(bookmark_object=post).save()

使用GenericReferenceFields的效率稍低于标准ReferenceFields,所以如果只引用一种文档类型,则更喜欢标准 ReferenceField

14. 唯一性约束

MongoEngine允许你通过提供unique=True给Field构造函数来指定一个字段在集合中是唯一的。如果您尝试将与唯一字段具有相同值的文档保存为数据库的文档, NotUniqueError则会引发。您也可以使用下列方法指定多字段唯一性约束:unique_with可以是单个字段名称,也可以是字段名称的列表或元组

class User(Document):
    username = StringField(unique=True)
    first_name = StringField()
    last_name = StringField(unique_with='first_name')

跳过文档验证保存
您也可以通过设置validate=False调用save() 方法时跳过整个文档验证过程

class Recipient(Document):
    name = StringField()
    email = EmailField()

recipient = Recipient(name='admin', email='root@localhost')
recipient.save()               # 会产生一个 ValidationError 错误
recipient.save(validate=False) # 不会

15.文档集合

通过在文档中添加meta类字典属性,我们可以更改数据集合的名称,例如下方的例子,我们把page改为了cmspage

class Page(Document):
    title = StringField(max_length=200, required=True)
    meta = {'collection': 'cmsPage'}

(五). 索引

您可以在集合上指定索引以加快查询速度。这是通过创建indexes在 meta字典中调用的索引规范列表完成的,其中索引规范可以是单个字段名称,包含多个字段名称的元组或包含完整索引定义的字典。

顺序可以通过在字段名前加上 +(用于升序)或-号(用于降序)来指定。请注意,方向只对多字段索引很重要。文本索引可以通过在字段名前添加一个$来指定。散列索引可以通过在字段名前添加#来指定:

class Page(Document):
    category = IntField()
    title = StringField()
    rating = StringField()
    created = DateTimeField()
    meta = {
        'indexes': [
            'title',
            '$title',  # 文本索引
            '#title',  # 散列索引
            ('title', '-rating'),
            ('category', '_cls'),
            {
                'fields': ['created'],
                'expireAfterSeconds': 3600
            }
        ]
    }

如果采用字典,那么以下选项可用:

fields (默认:无) 
要索引的字段。与上述相同的格式指定。
cls (默认值:True) 
如果您有多态模型可以继承并 allow_inheritance打开,则可以配置索引是否应该将该_cls字段自动添加到索引的开头。 
sparse (默认:False) 
索引是否应该是稀疏的。
unique (默认:False) 
索引是否应该是唯一的。
expireAfterSeconds (可选的) 
允许您通过设置以秒为单位的时间过期来自动将某个字段中的数据过期。

(六). 排序

可以为您QuerySet使用的ordering属性 指定默认排序。排序将在QuerySet创建时应用 ,并且可以通过随后的order_by()覆盖:

from datetime import datetime

class BlogPost(Document):
    title = StringField()
    published_date = DateTimeField()

    meta = {
        'ordering': ['-published_date']
    }

blog_post_1 = BlogPost(title="Blog Post #1")
blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0)

blog_post_2 = BlogPost(title="Blog Post #2")
blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0)

blog_post_3 = BlogPost(title="Blog Post #3")
blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0)

blog_post_1.save()
blog_post_2.save()
blog_post_3.save()

# 通过默认的排序(降序),获取第一篇文章
# from BlogPost.meta.ordering
latest_post = BlogPost.objects.first()
assert latest_post.title == "Blog Post #3"

# 覆盖原来的默认排序规则,采用升序
first_post = BlogPost.objects.order_by("+published_date").first()
assert first_post.title == "Blog Post #1"

(七). 文档继承

要创建您定义的文档的专用类型,您可以对其进行子类化并添加任何可能需要的额外字段或方法。 由于这是新类,它不是Document的直接子类,因此它不会存储在它自己的集合中; 它将使用与超类使用相同的集合。 这样可以更方便,更高效地检索相关文档 - 您只需在元数据中将allow_inheritance设置为True即可。

# Stored in a collection named 'page'
class Page(Document):
    title = StringField(max_length=200, required=True)

    meta = {'allow_inheritance': True}  #这里要设置为True,默认是False

# Also stored in the collection named 'page'
class DatedPage(Page):
    date = DateTimeField()

(八). 抽象类

如果您想为一组Document类添加一些额外的功能,但您不需要或不需要继承的开销,则可以使用该 abstract属性meta。这不会打开文档继承,但可以让您保持代码干燥:

    class BaseDocument(Document):
        meta = {
            'abstract': True,
        }
        def check_permissions(self):
            ...
    
    class User(BaseDocument):
       ...

现在,User类将有权访问继承的check_permissions方法,并且不会存储任何额外的_cls信息 这个特性在实际的web开发中作用很大

标签:name,python,Mongodb,StringField,MongoEngine,文档,connect,Document,class
来源: https://blog.csdn.net/qq_38923792/article/details/100182474