zoukankan      html  css  js  c++  java
  • MongoEngine 中文文档 系列教程 User Guide( 二)

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

        定义一个文档纲要

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

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

    [python]
     
    1. from mongoengine import *  
    2. import datetime  
    3.   
    4. class Page(Document):  
    5.     title = StringField(max_length=200, required=True)  
    6.     date_modified = DateTimeField(default=datetime.datetime.now)  

         动态文档纲要

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

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

    [python] 
     
    1. from mongoengine import *  
    2.   
    3. class Page(DynamicDocument):  
    4.     title = StringField(max_length=200, required=True)  
    5.   
    6. # Create a new page and add tags  
    7. >>> page = Page(title='Using MongoEngine')  
    8. >>> page.tags = ['mongodb', 'mongoengine']a>>> page.save()  
    9.   
    10. >>> Page.objects(tags='mongoengine').count()  
    11. >>> 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.   
    5. class ExampleSecond(Document):  
    6.     # Default a set of values  
    7.     values = ListField(IntField(), default=lambda: [1,2,3])  
    8.   
    9. class ExampleDangerous(Document):  
    10.     # This can make an .append call to  add values to the default (and all the following objects),  
    11.     # instead to just an object  
    12.     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.   
    7.   
    8. class Shirt(Document):  
    9.     size = StringField(max_length=3, choices=SIZE)  

    或者只包含值的也可以

    [python]
     
    1. SIZE = ('S', 'M', 'L', 'XL', 'XXL')  
    2.   
    3. class Shirt(Document):  
    4.     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.   
    4. comment1 = Comment(content='Good work!')  
    5. comment2 = Comment(content='Nice article!')  
    6. page = Page(comments=[comment1, comment2])  


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

    [python]
     
    1. class SurveyResponse(Document):  
    2.     date = DateTimeField()  
    3.     user = ReferenceField(User)  
    4.     answers = DictField()  
    5.   
    6. survey_response = SurveyResponse(date=datetime.now(), user=request.user)  
    7. response_form = ResponseForm(request.POST)  
    8. survey_response.answers = response_form.cleaned_data()  
    9. survey_response.save()  


    引用字段

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

    [python]
     
    1. class User(Document):  
    2.     name = StringField()  
    3.   
    4. class Page(Document):  
    5.     content = StringField()  
    6.     author = ReferenceField(User)  
    7.   
    8. john = User(name="John Smith")  
    9. john.save()  
    10.   
    11. post = Page(content="Test Page")  
    12. post.author = john  
    13. 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.   
    6. class ProfilePage(Document):  
    7.     content = StringField()  


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

    [python]
     
    1. class User(Document):  
    2.     name = StringField()  
    3.   
    4. class Page(Document):  
    5.     content = StringField()  
    6.     authors = ListField(ReferenceField(User))  
    7.   
    8. bob = User(name="Bob Jones").save()  
    9. john = User(name="John Smith").save()  
    10.   
    11. Page(content="Test Page", authors=[bob, john]).save()  
    12. Page(content="Another Page", authors=[john]).save()  
    13.   
    14. # Find all pages Bob authored  
    15. Page.objects(authors__in=[bob])  
    16.   
    17. # Find all pages that both Bob and John have authored  
    18. 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.   
    4. class Post(Document):  
    5.     title = StringField()  
    6.   
    7. class Bookmark(Document):  
    8.     bookmark_object = GenericReferenceField()  
    9.   
    10. link = Link(url='http://hmarr.com/mongoengine/')  
    11. link.save()  
    12.   
    13. post = Post(title='Using MongoEngine')  
    14. post.save()  
    15.   
    16. Bookmark(bookmark_object=link).save()  
    17. 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.   
    5. recipient = Recipient(name='admin', email='root@localhost ')  
    6. recipient.save()               # will raise a ValidationError while  
    7. 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.   
    3. class BlogPost(Document):  
    4.     title = StringField()  
    5.     published_date = DateTimeField()  
    6.   
    7.     meta = {  
    8.         'ordering': ['-published_date']  
    9.     }  
    10.   
    11. blog_post_1 = BlogPost(title="Blog Post #1")  
    12. blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0)  
    13.   
    14. blog_post_2 = BlogPost(title="Blog Post #2")  
    15. blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0)  
    16.   
    17. blog_post_3 = BlogPost(title="Blog Post #3")  
    18. blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0)  
    19.   
    20. blog_post_1.save()  
    21. blog_post_2.save()  
    22. blog_post_3.save()  
    23.   
    24. # get the "first" BlogPost using default ordering  
    25. # from BlogPost.meta.ordering  
    26. latest_post = BlogPost.objects.first()  
    27. assert latest_post.title == "Blog Post #3"  
    28.   
    29. # override default ordering, order BlogPosts by "published_date"  
    30. first_post = BlogPost.objects.order_by("+published_date").first()  
    31. 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.   
    7.     meta = {  
    8.         'shard_key': ('machine', 'timestamp',)  
    9.     }  

    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.   
    5.     meta = {'allow_inheritance': True}  
    6.   
    7. # Also stored in the collection named 'page'  
    8. class DatedPage(Page):  
    9.     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.     }  
  • 相关阅读:
    Forest Program(dfs方法---树上的环)
    RMQ+差分处理(Let Them Slide)Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)
    线段树维护最后一个0的位置(Restore Permutation)Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)
    n*n矩阵 每行每列XOR为0(思维)
    区间DP(入门)括号匹配
    Dijkstra(模板)
    线段树--扫描线(模板)自下而上
    后缀数组 LCP--模板题
    状压DP--Rotate Columns (hard version)-- Codeforces Round #584
    01背包方案数(变种题)Stone game--The Preliminary Contest for ICPC Asia Shanghai 2019
  • 原文地址:https://www.cnblogs.com/fillim/p/4864523.html
Copyright © 2011-2022 走看看