zoukankan      html  css  js  c++  java
  • mongoengine简单使用

    创建model

    class User(Document):
        email = StringField(required=True)
        first_name = StringField(max_length=50)
        last_name = StringField(max_length=50)

    文档都是继承Document类。
    预留:
    字段类型:

    1. StringField,字符串。
    2. ListField,列表。列表里还可以传入字段规定列表内的字段类型,例如ListField(StringField(max_length=30))
    3. ReferenceField, 这是一个保存相关文档的filed
    4. StringFiled(regex=None,max_length=None,min_lenght=None) #字符串类型
    5. IntField(min_value=None,max_value=None) #整数类型
    6. FloatField(min_value=None,max_value=None) #字符串类型
    7. BooleanField() #布尔类型
    8. DateTimeField() #时间类型
    9. listField() #可以插入列表的
    10. DictField() #字典类型
    11. ReferenceField() #参照类型
    12. SequenceField() #自动产生一个数列、 递增的

    字段限制:

    1. required,必须的。
    2. max_length,最大长度。
    3. default #默认值 也可以是一个函数 可调用类型
    4. primary_key #插入数据是否重复
    5. null #赋值是否可以为空
    6. choices #列表的范围
    7. unique #当前field只能是唯一的

    继承model

    class Post(Document):
        title = StringField(max_length=120, required=True)
        author = ReferenceField(User)
    
        meta = {'allow_inheritance': True}
    
    class TextPost(Post):
        content = StringField()
    
    class ImagePost(Post):
        image_path = StringField()
    
    class LinkPost(Post):
        link_url = StringField()

    只需要再父类的meta的 'allow_inheritance' 设置为True

    内嵌文档

    一篇文章post内的所有评论comments直接嵌入这篇文章的内部是mongodb常见的思路,首先创建一个comments的model:

    class Comment(EmbeddedDocument):
        content = StringField()
        name = StringField(max_length=120)

    这个model继承了EmbeddedDocument类,声明了他就是一个内嵌的文档,它不会在数据库里有个专门的文档保存,例如post这样的文档。post的model应该这样写:

    class Post(Document):
        title = StringField(max_length=120, required=True)
        author = ReferenceField(User)
        tags = ListField(StringField(max_length=30))
        comments = ListField(EmbeddedDocumentField(Comment))

    关联删除

    ReferenceField这个字段似乎是一个外键,创建model是指定reverse_delete_rule的值,可以设定关联删除的规则,如:

    class Post(Document):
        title = StringField(max_length=120, required=True)
        author = ReferenceField(User, reverse_delete_rule=CASCADE)
        tags = ListField(StringField(max_length=30))
        comments = ListField(EmbeddedDocumentField(Comment))

    此例中reverse_delete_rule的值设定为CASCADE,这样如果删除了一个用户,用户下所有的关联的post都会被删除。更多字段查询ReferenceField API

    添加数据

    现在我们可以添加数据了,写法有两种。如:

    ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save()

    再如:

    ross = User(email='ross@example.com')
    ross.first_name = 'Ross'
    ross.last_name = 'Lawley'
    ross.save()

    以上两种都是可以的。注意它可以用属性赋值的语法。如果save()了之后,又修改了属性,再save()就可以了,非常方便。

    使用数据

    添加几个数据之后,我们可以来使用这些数据了。所有的model,例如上面的Post,User,都有一个objects属性,可以检索出此类下所有的文档。例如我们用继承自Post类的TextPost做个最简单的示范。

    for post in TextPost.objects:
        print post.content

    这样会print出所有属于TextPost的文档的content。如果用Post.objects,可以得到所有属于Post或者继承自Post的model实例化的文档,这是一个QuerySet对象,(类似一个游标对象,不知道对不对)。如:

    for post in Post.objects:
        print post.title
        print '=' * len(post.title)
    
        if isinstance(post, TextPost):
            print post.content
    
        if isinstance(post, LinkPost):
            print 'Link:', post.link_url

    可见objects的类是不一样的,只是父类都是Post。
    检索指定字段的值的文档也很方便,例:

    for post in Post.objects(tags='mongodb'):
        print post.title

    这样就能检索出tags里有'mongodb'的文档。我觉得很方便,普通的查询条件如name='foo'这样的写法很正常,但tags是一个列表,用这样的写法,直接给个值他就能自己遍历判断是否是列表里的一个值,隐去了列表这个细节,很赞。
    objects对象和sqlAlchemy的游标一样,也可以调用first()或者count(),意思都是一样。如:

    num_posts = Post.objects(tags='mongodb').count()
    print 'Found %d posts with tag "mongodb"' % num_posts

    连接数据库

    mongoengine连接数据可也很方便,最简单的传入数据库名如:

    from mongoengine import connect
    connect('project1')
    

    connect还接收, name(数据库名),username,password,host等参数。

    配置多个数据库

    新建model的时候可以指定model可以属于哪个数据库,可以都添加进来例:

    class User(Document):
        name = StringField()
    
        meta = {"db_alias": "user-db"}  # 根据元属性确认哪一个数据库
    
    class Book(Document):
        name = StringField()
    
        meta = {"db_alias": "book-db"}# 根据元属性确认哪一个数据库
    class AuthorBooks(Document): 
      author
    = ReferenceField(User)
      book
    = ReferenceField(Book)
      
    meta
    = {"db_alias": "users-books-db"} # 根据元属性确认哪一个数据库

    不过这些数据库先要用connet连接。

    临时转换数据库database(未知,没用过)

    model表虽然设计了存储的数据库,但是有些数据需要存储到另一个数据库怎么办。可以转换:

    from mongoengine.context_managers import switch_db
    
    class User(Document):
        name = StringField()
    
        meta = {"db_alias": "user-db"}
    
    with switch_db(User, 'archive-user-db') as User:
        User(name="Ross").save()  # Saves the 'archive-user-db'

    这样,应该默认保存在“user-db”这个数据库里的User,现在是作为“archive-user-db”的User保存了。这是跨db的保存。

    临时转换集合collection

    同样mongoegnine也支持collection的转换, 如:

    from mongoengine.context_managers import switch_collection
    
    class Group(Document):
        name = StringField()
    
    Group(name="test").save()  # Saves in the default db
    
    with switch_collection(Group, 'group2000') as Group:
        Group(name="hello Group 2000 collection!").save()  # Saves in group2000 collection

    保存到里一个集合,第一个参数为需要转换的model,第二个为目标集合名,最后的model名可以不变。

    Reference fields

    这是一个设置相关document的fields,如:

    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()

    这样就可以引用了。

    一对多关系

    可以用ListField保存ReferenceField的方法来保存一对多的关系。例:

    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])
    
    # Remove Bob from the authors for a page.
    Page.objects(id='...').update_one(pull__authors=bob)
    
    # Add John to the authors for a page.
    Page.objects(id='...').update_one(push__authors=john)

    一次插入和删除一条关联可见最后两行,多个的方法还不知道。

    如果关联的文档发生了变化,如何同步改变呢?例如ReferenceField在子级的多对一,一对一关系,如何能删除了关联的父级,子级也一并删除呢?

    class Parent(Document):
        name = StringField()
        
    class Child(Document):
        name = StringField()
        parent = ReferenceField(Parent, reverse_delete_rule=CASCADE)

    定义一个反向删除的规则,CASCADE,就能一并删除了。

    其实用这样的多对一的方式做到一对多并不方便,这个应用场景更多是一对一的关联。那如何方便的一对多呢。ListField:

    class Child(Document):
        name = StringField()
    
    class Parent(Document):
        name = StringField()
        child = ListField(ReferenceField(Child, reverse_delete_rule=PULL))

    首先子级要先定义,应为父级的ReferenceField要传入子级的类。父级定义一个列表保存所有子级ReferenceField。反向删除规则为PULL,这样删除一个child,他的关联就会自动从父级的ListField中删掉了。

    另外还有DO_NOTHING,DENY,NULLIFY。

    另外GenericReferenceField不需要传入关联类,可以任意指定。

    文档名

    这个库默认保存的文档是定义的类的缩写加下划线,可以用meta = {'collection': 'YourDocument'}来设置你想要的文档名称

    限制文档数量和大小

    如果需要限制一个文档的数量或者大小,可以用meta = {'max_documents': 1000, 'max_size': 2000000}

    排序

    再meta中放入ordering,可以指定文档的默认排序方式,例如meta = {'ordering': ['-published_date']},不过order_by()方法的优先级更高。

    clean方法

    文档调用save()时,如果定义了clean,会先调用clean,如:

    class Essay(Document):
        status = StringField(choices=('Published', 'Draft'), required=True)
        pub_date = DateTimeField()
    
        def clean(self):
            """Ensures that only published essays have a `pub_date` and
            automatically sets the pub_date if published and not set"""
            if self.status == 'Draft' and self.pub_date is not None:
                msg = 'Draft entries should not have a publication date.'
                raise ValidationError(msg)
            # Set the pub_date for published items if not set.
            if self.status == 'Published' and self.pub_date is None:
                self.pub_date = datetime.now()

    检索

    操作符

    ne – not equal to,不等于
    lt – less than,小于
    lte – less than or equal to,小于或等于
    gt – greater than,大于
    gte – greater than or equal to,大于或等于
    not – negate a standard check, may be used before other operators (e.g. Q(age__not__mod=5)),否,与其他操作符一起用
    in – value is in list (a list of values should be provided),提供一个列表,数据库值在列表内
    nin – value is not in list (a list of values should be provided),提供一个列表,数据库值不在列表内
    mod – value % x == y, where x and y are two provided values,整除
    all – every item in list of values provided is in array,提供的列表元素都在数据库数组内
    size – the size of the array is,数据库数组的大小
    exists – value for field exists,field存在

    字符串检索

    exact – string field exactly matches value,完全匹配
    iexact – string field exactly matches value (case insensitive),完全匹配,不区分大小写
    contains – string field contains value,包含
    icontains – string field contains value (case insensitive),包含,不区分大小写
    startswith – string field starts with value,开始于
    istartswith – string field starts with value (case insensitive),开始于,不区分大小写
    endswith – string field ends with value,结束于
    iendswith – string field ends with value (case insensitive),结束于,不区分大小写
    match – performs an $elemMatch so you can match an entire document within an array,匹配?

    列表检索

    如果一个域是个列表,同样可以传入一个元素,这样做的意思是检索所有这个列表域中包含这个元素的文档,例:

    class Page(Document):
        tags = ListField(StringField())
    
    # This will match all pages that have the word 'coding' as an item in the
    # 'tags' list
    Page.objects(tags='coding')

    当然也可以指定这个列表域的序号来匹配,如:

    Page.objects(tags__0='db')

    这样即是匹配列表域第一个元素为‘db’的文档

    如果对列表检索的内的结果需要分割,可如:

    # comments - skip 5, limit 10
    Page.objects.fields(slice__comments=[5, 10])

    检索结果的分割和限制

    可以使用limit()和skip()方法,但是比较好的是:

    # Only the first 5 people
    users = User.objects[:5]
    
    # All except for the first 5 people
    users = User.objects[5:]
    
    # 5 users, starting from the 11th user found
    users = User.objects[10:15]

    定义默认的搜索条件

    可以定义自己的object方法:

    class BlogPost(Document):
        title = StringField()
        date = DateTimeField()
    
        @queryset_manager
        def objects(doc_cls, queryset):
            # This may actually also be done by defining a default ordering for
            # the document, but this illustrates the use of manager methods
            return queryset.order_by('-date')

    这样可行,但是也别闲着没事干去改默认的函数,还是自己定义一个吧:

    class BlogPost(Document):
        title = StringField()
        published = BooleanField()
    
        @queryset_manager
        def live_posts(doc_cls, queryset):
            return queryset.filter(published=True)
    
    BlogPost(title='test1', published=False).save()
    BlogPost(title='test2', published=True).save()
    assert len(BlogPost.objects) == 2
    assert len(BlogPost.live_posts()) == 1

    聚合

    计算一个域的总和,使用sum():

    yearly_expense = Employee.objects.sum('salary')

    计算一个域的平均值,使用average():

    mean_age = User.objects.average('age')

    使用only()提高检索效率

    使用only()只会得到文档中的某个域,以及是默认值的域,如:

    class Film(Document):
         title = StringField()
         year = IntField()
         rating = IntField(default=3)
    
    Film(title='The Shawshank Redemption', year=1994, rating=5).save()
    f = Film.objects.only('title').first()
    f.title    # 'The Shawshank Redemption'
    f.year   # None 
    f.rating # default value  3

    exclude()是和它相反的方法。
    另外,如果用了only(),之后有需要其他的值了,可以使用reload()

    原子更新

    可以使用update_one(), update() 和 modify() 方法对 QuerySet 进行更新,和使用modify() 和 save() 对一个文档进行更新。下面是一些搭配使用的修改符:

    setset a particular value,设置某个值
    unset – delete a particular value (since MongoDB v1.3),删除某个值
    inc – increment a value by a given amount,增加
    dec – decrement a value by a given amount,减少
    push – append a value to a list,增加列表一个值
    push_all – append several values to a list,增加列表多个值(传入一个列表)
    pop – remove the first or last element of a list depending on the value,取出一个列表最前或最后的值
    pull – remove a value from a list,删除一个列表的值
    pull_all – remove several values from a list,删除一个列表多个值
    add_to_set – add value to a list only if its not in the list already,加入列表一个值,如果它之前不在其中。

    这些修改符和检索操作符用法差不多,只是他们在域名之前:

    post = BlogPost(title='Test', page_views=0, tags=['database'])
    post.save()
    BlogPost.objects(id=post.id).update_one(inc__page_views=1)
    post.reload()  # the document has been changed, so we need to reload it
    post.page_views  # 1
    
    BlogPost.objects(id=post.id).update_one(set__title='Example Post')
    post.reload()
    post.title  # 'Example Post'
    
    BlogPost.objects(id=post.id).update_one(push__tags='nosql')
    post.reload()
    post.tags  # ['database', 'nosql']

    如果没有指定修改符,那么默认是set,所以以下两句实际是相同的:

    BlogPost.objects(id=post.id).update(title='Example Post')
    BlogPost.objects(id=post.id).update(set__title='Example Post')
    常用查询方法
    https://blog.csdn.net/weixin_42042680/article/details/87909424
    个人实际示例
    https://www.cnblogs.com/clbao/p/11428794.html
  • 相关阅读:
    例题三、简单的分支与循环结构
    第七章例题、心得及问题。
    第六章例题、心得及问题。
    第五章例题、心得及问题。
    第四章例题、心得及问题。
    第三章例题、心得及问题。
    第一章、第二章的心得及问题。
    实验3 分支结构。
    关于我最后一篇不是心得的 心得。
    关于第五章还没看完之后的 心得。
  • 原文地址:https://www.cnblogs.com/clbao/p/11640658.html
Copyright © 2011-2022 走看看