MongoEngine文档 新手教程 定义文档

Stella981
• 阅读 520

在MongoDB里面,一条文档大致相当于关系型数据库里面的一行。在关系型数据库里面,行是被存储在表里面,并且有一个严格的结构。MongoDB里面把文档存储在集合里面而不是存在表里面,最根本上的不同就是在数据库层面上没有强制的结构限制。

    定义一个文档纲要

    MongoEngine允许你为文档定义一个纲要这可以帮你减少编码错误,让你利用现有的字段来定义各种功能的函数。

    定义一个文档的纲要,首先需要创建一个继承 Document 的类。文档的字段都被作为这个继承类的属性。

[python]  

  1. from mongoengine import *

  2. import datetime

  3. class Page(Document):

  4. title = StringField(max_length=200, required=True)

  5. date_modified = DateTimeField(default=datetime.datetime.now)

动态文档纲要

    MongoDB的一个好处就是为集合定义动态的纲要,这在有动态文档的场景下能让数据有组织,有计划的存储。

    动态文档与一般文档的工作方式一样,但是任何为它们设置的数据或属性也会被存储

[python] 

  1. from mongoengine import *

  2. class Page(DynamicDocument):

  3. title = StringField(max_length=200, required=True)

  4. Create a new page and add tags

  5. >>> page = Page(title='Using MongoEngine')

  6. >>> page.tags = ['mongodb', 'mongoengine']a>>> page.save()

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

  8. >>> 1

字段

    在默认情况下,字段可以不需要。让一个字段强制存在,可以将这个字段的 require 关键字设置为 true 。字段也可以有验证限制。字段也可以设置默认值,在字段没有被提供值的时候会用到。可以使用的字段类型如下:

字段参数

    db_field(default: None)

        mongodb字段名

    name(default: None)

       mongoengine字段名

    required(default: False)

       如果设置为True,那么在存储数据的时候,如果这个字段为空,在验证的时候会产生ValidateError。

    default(default: None)

       为字段设置默认值。

       default这个参量的定义是遵循 the general rules on Python,实例如下:

[python] 

  1. class ExampleFirst(Document):

  2. Default an empty list

  3. values = ListField(IntField(), default=list)

  4. class ExampleSecond(Document):

  5. Default a set of values

  6. values = ListField(IntField(), default=lambda: [1,2,3])

  7. class ExampleDangerous(Document):

  8. This can make an .append call to  add values to the default (and all the following objects),

  9. instead to just an object

  10. values = ListField(IntField(), default=[1,2,3])

unique(default: False)

       如果设置为True,那么同一个collection里面不能有一个document与它的值相同。

    unique_with(default: None)

       让一个字段与该字段一同设置为unique

    primary_key(default: False)

        如果设置为True,那么这个字段被设置为主键。

    choices(default: None)

       当字段的值需要限制的时候设置一个可迭代的list或者tuple,也可以是一个嵌套的tuple。

[python]  

  1. SIZE = (('S', 'Small'),

  2. ('M', 'Medium'),

  3. ('L', 'Large'),

  4. ('XL', 'Extra Large'),

  5. ('XXL', 'Extra Extra Large'))

  6. class Shirt(Document):

  7. size = StringField(max_length=3, choices=SIZE)

或者只包含值的也可以

[python]  

  1. SIZE = ('S', 'M', 'L', 'XL', 'XXL')

  2. class Shirt(Document):

  3. size = StringField(max_length=3, choices=SIZE)

help_text(default: None)

      在使用这个字段的时候输出帮助---在使用表单的时候用到。

    verbose_name(default: None)

      为这个字段起更能让人接受的名字---在使用表单的时候用到。

列表字段

mongodb里面允许你存储list。给document添加一个list字段,使用ListField类型,ListField把另一个类型的对象作为它的第一个参数,用来指定在list里面存储的数据类型。

[python]  

  1. class Page(Document):

  2. tags = ListField(StringField(max_length=50))

嵌入的document
mongodb能够存储嵌入的document。需要为这些document定义Schemata,它们也是规范的document。

定义嵌入的document的时候,像往常一样定义一个document,但是要继承EmbeddedDocument而不是Document

[python]  

  1. class Comment(EmbeddedDocument):

  2. content = StringField()

在document中嵌入另一个document,使用 EmbeddedDocumentField 类型。第一个参数是嵌入document的类:

[python]  

  1. class Page(Document):

  2. comments = ListField(EmbeddedDocumentField(Comment))

  3. comment1 = Comment(content='Good work!')

  4. comment2 = Comment(content='Nice article!')

  5. page = Page(comments=[comment1, comment2])

字典字段
通常,会使用嵌入document来代替字典----总的来说字典不支持任何类型检查与约束。可是,有时候你不知道你想存储的数据是什么类型,这时候使用 DictField会比较合适:

[python]  

  1. class SurveyResponse(Document):

  2. date = DateTimeField()

  3. user = ReferenceField(User)

  4. answers = DictField()

  5. survey_response = SurveyResponse(date=datetime.now(), user=request.user)

  6. response_form = ResponseForm(request.POST)

  7. survey_response.answers = response_form.cleaned_data()

  8. survey_response.save()

引用字段

引用字段用来引用在数据库里面存储的其他document,使用 ReferenceField  ,在构造器中把另一个document的类作为第一个参数,然后就可以简单地指定document到这个字段。

[python]  

  1. class User(Document):

  2. name = StringField()

  3. class Page(Document):

  4. content = StringField()

  5. author = ReferenceField(User)

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

  7. john.save()

  8. post = Page(content="Test Page")

  9. post.author = john

  10. post.save()

User对象自动成为了引用类,在检索Page的时候会间接引用User。

当引用字段引用的是自身的时候,在ReferenceField 的构造器中添加 'self' 作为它的参数,如果引用还未定义的document,则使用应用类的类名作为构造器参数:

[python]  

  1. class Employee(Document):

  2. name = StringField()

  3. boss = ReferenceField('self')

  4. profile_page = ReferenceField('ProfilePage')

  5. class ProfilePage(Document):

  6. content = StringField()

使用ListField的一对多
如果你想利用一个引用的list来实现一对多,那么引用会被存储成  DBRefs ,那么需要查询的时候,你也需要通过这个对象来进行查询。

[python]  

  1. class User(Document):

  2. name = StringField()

  3. class Page(Document):

  4. content = StringField()

  5. authors = ListField(ReferenceField(User))

  6. bob = User(name="Bob Jones").save()

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

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

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

  10. Find all pages Bob authored

  11. Page.objects(authors__in=[bob])

  12. Find all pages that both Bob and John have authored

  13. Page.objects(authors__all=[bob, john])

处理删除引用的document

默认情况下,mongodb不会检查数据的完整性,如果删除了其他document正在引用的document会引发一致性的问题。mongoengine的ReferenceField 添加了一些功能来维持数据一致性,为没一个引用提供了删除规则。删除规则通过声明ReferenceField 的reverse_delete_rule 属性来指定,就像这样:

[python]  

  1. class Employee(Document):

  2. ...

  3. profile_page = ReferenceField('ProfilePage', reverse_delete_rule=mongoengine.NULLIFY)

这个例子中的声明定义了如果一个Employee对象删除,与它关联的ProfilePage也会删除。如果一批Employee对象被删除,那么与它关联的ProfilePage也会被删除。

它的值也可以被设置成如下的常量:

mongoengine.DO_NOTHING

这是默认值不做任何事。在删除的时候速度比较快,但是会带来数据不一致和空引用的问题。

mongoengine.DENY

如果仍有document引用到这个对象,那么会阻止删除

mongoengine.NULLIFY

任何对象的字段关联到这个对象的如果被删除,那么这个document也会被删除,关联关系作废。

mongoengine.CASCADE

任何对象的字段引用到这个对象的会被先删除

mongoengine.PULL

移除对于对象的引用关系

通用引用字段

一种次选的引用字段也是存在的, GenericReferenceField 。它可以让你引用任何类型的document,因此它不用其他document的类来作为它的参数:

[python]  

  1. class Link(Document):

  2. url = StringField()

  3. class Post(Document):

  4. title = StringField()

  5. class Bookmark(Document):

  6. bookmark_object = GenericReferenceField()

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

  8. link.save()

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

  10. post.save()

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

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

唯一性约束
mongoengine里面允许你制定字段在collection里面的值是唯一的,通过在构造器里面指定  unique=True

如果你想在数据库里存储已存在的value的document,会引发OperationError。你也可以通过使用unique_with来设置多字段的唯一性约束,它的值可以是一个字段名,或者是存储了字段名的list或tuple。

[python]  

  1. class User(Document):

  2. username = StringField(unique=True)

  3. first_name = StringField()

  4. last_name = StringField(unique_with='first_name')

在保存时跳过document验证

你可以在使用save()的时候通过设置validate=False 来在保存的时候跳过验证

[python]  

  1. class Recipient(Document):

  2. name = StringField()

  3. email = EmailField()

  4. recipient = Recipient(name='admin', email='root@localhost ')

  5. recipient.save()               # will raise a ValidationError while

  6. recipient.save(validate=False) # won't

Document Collection

document对象是直接继承于Document ,会在数据库里面拥有它们自己的collection。这个collection的名字默认就是类的名字,被转化成了小写。如果你想改变这个collection的名字,可以在类里面创建一个字典属性叫meta,然后可以随意设置这个collection的名字了。

[python]  

  1. class Page(Document):

  2. title = StringField(max_length=200, required=True)

  3. meta = {'collection': 'cmsPage'}

索引
你 可以在document里面指定索引来使查询的时候速度更快。这个可以通过在meta字典里声明一个叫键为 'indexes', 值为存放索引规则的list的键值对来实现,一个索引规则可以是一个字段名,或者是由几个字段名组成的tuple,也可以是一个包含完整索引声明的字典。 可以在字段名前面加上+ 或者-来指定索引的顺序。这只会在联合索引中有效果。

[python]  

  1. class Page(Document):

  2. title = StringField()

  3. rating = StringField()

  4. meta = {

  5. 'indexes': ['title', ('title', '-rating')]

  6. }

meta字典中还有一下的选项可选:

fields (Default: None)
        产生索引的字段,声名的方法与以上一样。

types (Default: True)

索引是否应该添加 _type字段

sparse (Default: False)

索引是否需要备份

unique (Default: False)

索引是否需要备份

地理空间索引

地理空间索引会自动为所有的 GeoPointField  创建。

也可以来明确地指定地理空间索引。这在你需要定义一个 DictField  的子域或者自己定制的包含一个点的字段的索引的时候很有用。创建地理空间索引的时候需要在字段名前面加 *:

[python]  

  1. class Place(Document):

  2. location = DictField()

  3. meta = {

  4. 'indexes': [

  5. '*location.point',

  6. ],

  7. }

顺序
在meta里面设置ordering的值可以指定你的 QuerySet  的默认顺序。在 QuerySet 被创建的时候顺序规则会被应用 ,它也可以通过使用 order_by() 来复写。

[python]  

  1. from datetime import datetime

  2. class BlogPost(Document):

  3. title = StringField()

  4. published_date = DateTimeField()

  5. meta = {

  6. 'ordering': ['-published_date']

  7. }

  8. blog_post_1 = BlogPost(title="Blog Post #1")

  9. blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0)

  10. blog_post_2 = BlogPost(title="Blog Post #2")

  11. blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0)

  12. blog_post_3 = BlogPost(title="Blog Post #3")

  13. blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0)

  14. blog_post_1.save()

  15. blog_post_2.save()

  16. blog_post_3.save()

  17. get the "first" BlogPost using default ordering

  18. from BlogPost.meta.ordering

  19. latest_post = BlogPost.objects.first()

  20. assert latest_post.title == "Blog Post #3"

  21. override default ordering, order BlogPosts by "published_date"

  22. first_post = BlogPost.objects.order_by("+published_date").first()

  23. assert first_post.title == "Blog Post #1"

共享键
如果你的collection是共享的,那么你需要指定一个存储共享键的tuple, 使用 mongoengine.Document.meta里面的shard_key属性,

[python]  

  1. class LogEntry(Document):

  2. machine = StringField()

  3. app = StringField()

  4. timestamp = DateTimeField()

  5. data = StringField()

  6. meta = {

  7. 'shard_key': ('machine', 'timestamp',)

  8. }

Document继承
为 了创建一个你定义的类型的document,你必须继承document并且加上一些你需要的字段和方法。如果这不是一个直接继承document的子 类,那么这个类的数据不会存放在自己的collection中,而会存放在它的父类的collection里面。这能在检索关联的document的时候 提供更多的方便。

[python]  

  1. Stored in a collection named 'page'

  2. class Page(Document):

  3. title = StringField(max_length=200, required=True)

  4. meta = {'allow_inheritance': True}

  5. Also stored in the collection named 'page'

  6. class DatedPage(Page):

  7. date = DateTimeField()

处理现有的数据
为 了改正这种层次的document涉及的检索,在数据库里面的document会存储另两个属性:_cls 和 _types。这些在mongoengine的接口中对用户是隐藏的,可能在使用mongoengine处理一个已经存在的数据库的时候不会呈现出来。你 可能会需要禁止这个类的继承,方法如下:

[python]  

  1. Will work with data in an existing collection named 'cmsPage'

  2. class Page(Document):

  3. title = StringField(max_length=200, required=True)

  4. meta = {

  5. 'collection': 'cmsPage',

  6. 'allow_inheritance': False,

  7. }

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
皕杰报表(关于日期时间时分秒显示不出来)
在使用皕杰报表设计器时,数据据里面是日期型,但当你web预览时候,发现有日期时间类型的数据时分秒显示不出来,只有年月日能显示出来,时分秒显示为0:00:00。1.可以使用tochar解决,数据集用selecttochar(flowdate,"yyyyMMddHH:mm:ss")fromtablename2.也可以把数据库日期类型date改成timestamp
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这