在MongoDB里面,一条文档大体至关于关系型数据库里面的一行。在关系型数据库里面,行是被存储在表里面,而且有一个严格的结构。MongoDB里面把文档存储在集合里面而不是存在表里面,最根本上的不一样就是在数据库层面上没有强制的结构限制。 html
定义一个文档纲要 python
MongoEngine容许你为文档定义一个纲要这能够帮你减小编码错误,让你利用现有的字段来定义各类功能的函数。 mongodb
定义一个文档的纲要,首先须要建立一个继承 Document 的类。文档的字段都被做为这个继承类的属性。 数据库
- from mongoengine import *
- import datetime
-
- class Page(Document):
- title = StringField(max_length=200, required=True)
- date_modified = DateTimeField(default=datetime.datetime.now)
动态文档纲要
MongoDB的一个好处就是为集合定义动态的纲要,这在有动态文档的场景下能让数据有组织,有计划的存储。 api
动态文档与通常文档的工做方式同样,可是任何为它们设置的数据或属性也会被存储 app
- 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']a>>> page.save()
-
- >>> Page.objects(tags='mongoengine').count()
- >>> 1
字段 ide
在默认状况下,字段能够不须要。让一个字段强制存在,能够将这个字段的 require 关键字设置为 true 。字段也能够有验证限制。字段也能够设置默认值,在字段没有被提供值的时候会用到。可使用的字段类型以下: 函数
字段参数
db_field(default: None) post
mongodb字段名 ui
name(default: None)
mongoengine字段名
required(default: False)
若是设置为True,那么在存储数据的时候,若是这个字段为空,在验证的时候会产生ValidateError。
default(default: None)
为字段设置默认值。
default这个参量的定义是遵循 the general rules on Python,实例以下:
- class ExampleFirst(Document):
- # Default an empty list
- values = ListField(IntField(), default=list)
-
- class ExampleSecond(Document):
- # Default a set of values
- values = ListField(IntField(), default=lambda: [1,2,3])
-
- class ExampleDangerous(Document):
- # This can make an .append call to add values to the default (and all the following objects),
- # instead to just an object
- 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。
- SIZE = (('S', 'Small'),
- ('M', 'Medium'),
- ('L', 'Large'),
- ('XL', 'Extra Large'),
- ('XXL', 'Extra Extra Large'))
-
-
- 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)
help_text(default: None)
在使用这个字段的时候输出帮助---在使用表单的时候用到。
verbose_name(default: None)
为这个字段起更能让人接受的名字---在使用表单的时候用到。
列表字段
mongodb里面容许你存储list。给document添加一个list字段,使用ListField类型,ListField把另外一个类型的对象做为它的第一个参数,用来指定在list里面存储的数据类型。
- class Page(Document):
- tags = ListField(StringField(max_length=50))
嵌入的document
mongodb可以存储嵌入的document。须要为这些document定义Schemata,它们也是规范的document。
定义嵌入的document的时候,像往常同样定义一个document,可是要继承EmbeddedDocument而不是Document:
- class Comment(EmbeddedDocument):
- content = StringField()
在document中嵌入另外一个document,使用
EmbeddedDocumentField 类型。第一个参数是嵌入document的类:
- class Page(Document):
- comments = ListField(EmbeddedDocumentField(Comment))
-
- comment1 = Comment(content='Good work!')
- comment2 = Comment(content='Nice article!')
- page = Page(comments=[comment1, comment2])
字典字段
一般,会使用嵌入document来代替字典----总的来讲字典不支持任何类型检查与约束。但是,有时候你不知道你想存储的数据是什么类型,这时候使用
DictField会比较合适:
- class SurveyResponse(Document):
- date = DateTimeField()
- user = ReferenceField(User)
- answers = DictField()
-
- survey_response = SurveyResponse(date=datetime.now(), user=request.user)
- response_form = ResponseForm(request.POST)
- survey_response.answers = response_form.cleaned_data()
- survey_response.save()
引用字段
引用字段用来引用在数据库里面存储的其余document,使用 ReferenceField ,在构造器中把另外一个document的类做为第一个参数,而后就能够简单地指定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()
User对象自动成为了引用类,在检索Page的时候会间接引用User。
当引用字段引用的是自身的时候,在ReferenceField 的构造器中添加 'self' 做为它的参数,若是引用还未定义的document,则使用应用类的类名做为构造器参数:
- class Employee(Document):
- name = StringField()
- boss = ReferenceField('self')
- profile_page = ReferenceField('ProfilePage')
-
- class ProfilePage(Document):
- content = StringField()
使用ListField的一对多
若是你想利用一个引用的list来实现一对多,那么引用会被存储成
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()
-
- # 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])
处理删除引用的document
默认状况下,mongodb不会检查数据的完整性,若是删除了其余document正在引用的document会引起一致性的问题。mongoengine的ReferenceField 添加了一些功能来维持数据一致性,为没一个引用提供了删除规则。删除规则经过声明ReferenceField 的reverse_delete_rule 属性来指定,就像这样:
- class Employee(Document):
- ...
- 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的类来做为它的参数:
- 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()
惟一性约束
mongoengine里面容许你制定字段在collection里面的值是惟一的,经过在构造器里面指定
unique=True
若是你想在数据库里存储已存在的value的document,会引起OperationError。你也能够经过使用unique_with来设置多字段的惟一性约束,它的值能够是一个字段名,或者是存储了字段名的list或tuple。
- class User(Document):
- username = StringField(unique=True)
- first_name = StringField()
- last_name = StringField(unique_with='first_name')
在保存时跳过document验证
你能够在使用save()的时候经过设置validate=False 来在保存的时候跳过验证
- class Recipient(Document):
- name = StringField()
- email = EmailField()
-
- recipient = Recipient(name='admin', email='root@localhost ')
- recipient.save() # will raise a ValidationError while
- recipient.save(validate=False) # won't
Document Collection
document对象是直接继承于Document ,会在数据库里面拥有它们本身的collection。这个collection的名字默认就是类的名字,被转化成了小写。若是你想改变这个collection的名字,能够在类里面建立一个字典属性叫meta,而后能够随意设置这个collection的名字了。
- class Page(Document):
- title = StringField(max_length=200, required=True)
- meta = {'collection': 'cmsPage'}
索引
你能够在document里面指定索引来使查询的时候速度更快。这个能够经过在meta字典里声明一个叫键为 'indexes', 值为存放索引规则的list的键值对来实现,一个索引规则能够是一个字段名,或者是由几个字段名组成的tuple,也能够是一个包含完整索引声明的字典。能够在字段名前面加上+ 或者-来指定索引的顺序。这只会在联合索引中有效果。
- class Page(Document):
- title = StringField()
- rating = StringField()
- meta = {
- 'indexes': ['title', ('title', '-rating')]
- }
meta字典中还有一下的选项可选:
fields (Default: None)
产生索引的字段,声名的方法与以上同样。
types (Default: True)
索引是否应该添加 _type字段
sparse (Default: False)
索引是否须要备份
unique (Default: False)
索引是否须要备份
地理空间索引
地理空间索引会自动为全部的 GeoPointField 建立。
也能够来明确地指定地理空间索引。这在你须要定义一个
DictField
的子域或者本身定制的包含一个点的字段的索引的时候颇有用。建立地理空间索引的时候须要在字段名前面加 *:
- class Place(Document):
- location = DictField()
- meta = {
- 'indexes': [
- '*location.point',
- ],
- }
顺序
在meta里面设置ordering的值能够指定你的
QuerySet
的默认顺序。在
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()
-
- # get the "first" BlogPost using default ordering
- # from BlogPost.meta.ordering
- latest_post = BlogPost.objects.first()
- assert latest_post.title == "Blog Post #3"
-
- # override default ordering, order BlogPosts by "published_date"
- first_post = BlogPost.objects.order_by("+published_date").first()
- assert first_post.title == "Blog Post #1"
共享键
若是你的collection是共享的,那么你须要指定一个存储共享键的tuple, 使用
mongoengine.Document.meta里面的shard_key属性,
- class LogEntry(Document):
- machine = StringField()
- app = StringField()
- timestamp = DateTimeField()
- data = StringField()
-
- meta = {
- 'shard_key': ('machine', 'timestamp',)
- }
Document继承
为了建立一个你定义的类型的document,你必须继承document而且加上一些你须要的字段和方法。若是这不是一个直接继承document的子类,那么这个类的数据不会存放在本身的collection中,而会存放在它的父类的collection里面。这能在检索关联的document的时候提供更多的方便。
- # Stored in a collection named 'page'
- class Page(Document):
- title = StringField(max_length=200, required=True)
-
- meta = {'allow_inheritance': True}
-
- # Also stored in the collection named 'page'
- class DatedPage(Page):
- date = DateTimeField()
处理现有的数据
为了改正这种层次的document涉及的检索,在数据库里面的document会存储另两个属性:_cls 和 _types。这些在mongoengine的接口中对用户是隐藏的,可能在使用mongoengine处理一个已经存在的数据库的时候不会呈现出来。你可能会须要禁止这个类的继承,方法以下:
- # Will work with data in an existing collection named 'cmsPage'
- class Page(Document):
- title = StringField(max_length=200, required=True)
- meta = {
- 'collection': 'cmsPage',
- 'allow_inheritance': False,
- }