MongoEngine中文參考


 

在MongoDB里面,一條文檔大致相當於關系型數據庫里面的一行。在關系型數據庫里面,行是被存儲在表里面,並且有一個嚴格的結構。MongoDB里面把文檔存儲在集合里面而不是存在表里面,最根本上的不同就是在數據庫層面上沒有強制的結構限制。

定義一個文檔綱要

MongoEngine允許你為文檔定義一個綱要這可以幫你減少編碼錯誤,讓你利用現有的字段來定義各種功能的函數。

定義一個文檔的綱要,首先需要創建一個繼承 Document 的類。文檔的字段都被作為這個繼承類的屬性。

from mongoengine import *  

import datetime

 

class Page(Document):  

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

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

動態文檔綱要 MongoDB的一個好處就是為集合定義動態的綱要,這在有動態文檔的場景下能讓數據有組織,有計划的存儲。

動態文檔與一般文檔的工作方式一樣,但是任何為它們設置的數據或屬性也會被存儲

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

字段

在默認情況下,字段可以不需要。讓一個字段強制存在,可以將這個字段的 require 關鍵字設置為 true 。字段也可以有驗證限制。字段也可以設置默認值,在字段沒有被提供值的時候會用到。可以使用的字段類型如下:

BinaryField

BooleanField

ComplexDateTimeField

DateTimeField

DecimalField

DictField

DynamicField

EmailField

EmbeddedDocumentField

FileField

FloatField

GenericEmbeddedDocumentField

GenericReferenceField

GeoPointField

ImageField

IntField

ListField

MapField

ObjectIdField

ReferenceField

SequenceField

SortedListField

StringField

URLField

UUIDField

字段參數

db_field(default: None)

mongodb字段名

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,  

     }

 

mongoengine使用筆記

Sat 23 June 2012

最近重新拾起Django,但是Django並不支持mongodb,但是有一個模塊mongoengine可以實現Django Model類似的封裝.但是mongoengine的中文文檔幾乎沒有,有的也是簡短的幾句介紹和使用.下面我就分享一下我在使用過程中所記錄下的一些筆記,可能有點亂.大家可以參考一下.

安裝mongoengine

easy_install pymongo # 依賴庫

easy_install mongoengine

 

基本使用

from mongoengine import *

from datetime import datetime

# 連接數據庫

connect('blog')   # 連接本地blog數據庫

# 如需驗證和指定主機名

# connect('blog', host='192.168.3.1', username='root', password='1234')

 

# 定義分類文檔

class Categories(Document):

    ' 繼承Document類,為普通文檔 '

    name = StringField(max_length=30, required=True)

    artnum = IntField(default=0, required=True)

    date = DateTimeField(default=datetime.now(), required=True)

 

和Django的model使用很類似,所以也不解釋什么.

插入

cate = Categories(name="Linux")   # 如果requiredTrue則必須賦予初始值,如果有default,賦予初始值則使用默認值

cate.save() # 保存到數據庫

 

查詢和更新

文檔類有一個 objects 屬性.我們使用它來查詢數據庫.

# 返回集合里的所有文檔對象的列表

cate = Categories.objects.all()

 

# 返回所有符合查詢條件的結果的文檔對象列表

cate = Categories.objects(name="Python")

# 更新查詢到的文檔:

cate.name = "LinuxZen"

cate.update()

查詢數組 默認查詢數組"="代表的意思是in:

class Posts(Document):

    artid = IntField(required=True)

    title = StringField(max_length=100, required=True)

    content = StringField(required=True)

    author = ReferenceField(User)

    tags = ListField(StringField(max_length=20, required=True), required=True)

    categories = ReferenceField(Categories), required=True)

    comments = IntField(default=0, required=True)

 

# 將會返回所有tags包含coding的文檔

Posts.objects(tags='coding')

 

ReferenceField 引用字段:

通過引用字段可以通過文檔直接獲取引用字段引用的那個文檔:

class Categories(Document):

    name = StringField(max_length=30, required=True)

    artnum = IntField(default=0, required=True)

    date = DateTimeField(default=datetime.now(), required=True)

 

class Posts(Document):

 

    title = StringField(max_length=100, required=True)

    content = StringField(required=True)

    tags = ListField(StringField(max_length=20, required=True), required=True)

    categories = ReferenceField(Categories)

 

插入引用字段

cate =Categories(name="Linux")

cate.save()

post = Posts(title="Linuxzen.com", content="Linuxzen.com",tags=["Linux","web"], categories=cate)

post.save()

 

通過引用字段直接獲取引用文檔對象

一般文檔查詢會返回一個列表(盡管只有一個結果),我們想要獲得一個文檔對象可以使用索引獲取第一個文檔對象,但是mongoengine建議使用first()來獲取第一個:

>>> cate = Posts.objects.all().first().categories

>>> cate

 

>>> cate.name

u'Linux'

 

查詢包含Linux分類的文章

>>> cate = Categories.objects(name="Linux").first()

>>> Posts.objects(categories=cate)

 

EmbeddedDocument 嵌入文檔

繼承EmbeddedDocument的文檔類就是嵌入文檔,嵌入文檔用於嵌入其他文檔的EmbeddedDocumentField 字段,比如上面例子的tags字段如果改成嵌入文檔的話可以將Posts文檔類改成如下方式:

class Posts(Document):

 

    title = StringField(max_length=100, required=True)

    content = StringField(required=True)

    tags = ListField(EmbeddedDocumentField('Tags')required=True)

    categories = ReferenceField(Categories)

 

還需要添加一個Tags嵌入文檔類:

class Tags(EmbeddedDocument):

name = StringField()

date = DateTimeField(default=datetime.now())

 

我們像如下方式插入Posts文檔中的Tags

>>> tag = Tags(name="Linuxzen")

>>> post = Posts(title="Linuxzen.com", content="Linuxzen.com", tags=[tag], categories=cate)

>>> tag = Tags(name="mysite")

>>> post.tags.append(tag)

>>> post.save()

>>> tags = post.tags

>>> for tag in tags:

print tag.name

 

Linuxzen

mysite

 

時間段查詢

    start = datetime(int(year), int(month), 1)

    if int(month) + 1 > 12:

        emonth = 1

        eyear = int(year) + 1

    else:

        emonth = int(month) + 1

        eyear = int(year)

    end = datetime(eyear, emonth, 1)

    articles = Posts.objects(date__gte=start, date__lt=end).order_by('-date')

 

分片

slice用於分片

# comments - skip 5, limit 10

Page.objects.fields(slice__comments=[5, 10])

 

# 也可以使用索引值分片

 

# limit 5

users = User.objects[:5]

 

# skip 5

users = User.objects[5:]

 

# skip 10, limit 15

users = User.objects[10:15]

 

使用原始語句查詢

如果想使用原始的pymongo查詢方式可以使用__raw__操作符 Page.objects(raw={'tags':'coding'}) 使用$inc和$set操作符

# 更新嵌入文檔comments字段by的值為joe的文檔字段votes增加1

Page.objects(comments_by="joe").update(inc__votes=1)

 

# 更新嵌入文檔comments字段by的值為joe的文檔字段votes設置為1

Page.objects(comments_by="joe").update(set__votes=1)

 

其他技巧

#查詢結果轉換成字典

users_dict = User.objects().to_mongo()

 

# 排序,按日期排列

user = User.objects.order_by("date")

 

# 按日期倒序

 

user = User.objects.order_by("-date")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM