在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,
- }