pip install mongoengine from mongoengine import * connect(host="mongodb://127.0.0.1:27017/my_db") connect(host="mongodb://my_user:my_password@127.0.0.1:27017/my_db") connect( db='test', username='user', password='12345', host='mongodb://admin:qwerty@localhost/production')
该功能可用于断开特定链接。
from mongoengine import connect, disconnectconnect('a_db', alias='db1') class User(Document): name = StringField() meta = {'db_alias': 'db1'} disconnect(alias='db1') connect('another_db', alias='db1')
class User(Document): email = StringField(required=True) first_name = StringField(max_length=50) last_name = StringField(max_length=50) meta = {'db_alias': 'user-db-alias'} from mongoengine import * import datetime class Page(Document): title = StringField(max_length=200, required=True) date_modified = DateTimeField(default=datetime.datetime.utcnow)
若根据以后的需求增加不同的字段内容,可使用以下继承的方式:
connect(alias='user-db-alias', db='user-db') connect(alias='book-db-alias', db='book-db') connect(alias='users-books-db-alias', db='users-books-db') class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) meta = {'allow_inheritance': True,'collection':'cmsPost'} class TextPost(Post): content = StringField() class ImagePost(Post): image_path = StringField() class LinkPost(Post):
动态文档的工作方式与文档相同,但设置给他们的任何数据/属性也将保存
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'] >>> page.save() >>> Page.objects(tags='mongoengine').count()>>> 1
如果要使用标签,将标签作为单独的字段,存储到数据库中
class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30))
MongoDB能够在其他文档中嵌入文档。可以为这些嵌入式文档定义概要,就像他们可能用于常规文档一样。要创建嵌入文档,只需像往常一样定义一个文档,继承 EmbeddedDocument而不是Document。 要将文档嵌入到另一个文档中,请使用EmbeddedDocumentField字段类型,将嵌入的文档作为第一个参数提供。
class Comment(EmbeddedDocument): content = StringField() name = StringField(max_length=120) # 我们可以在帖子文档中存储评论文档列表 class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30)) comments = ListField(EmbeddedDocumentField(Comment))
class ProfilePage(Document): ... employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE) #这个例子中的声明意味着当一个Employee对象被移除时,ProfilePage引用该雇员的那个也被移除。如果删除了整批员工,则所有链接的配置文件页面也会被删除
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()
for post in Post.objects: print(post.title)
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: {}'.format(post.link_url))
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])
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) class User(Document): username = StringField(unique=True) first_name = StringField() last_name = StringField(unique_with='first_name')
def _not_empty(val): if not val: raise ValidationError('value can not be empty') class Person(Document): name = StringField(validation=_not_empty)
也可以通过设置validate=False,调用save()方法时跳过整个文档验证过程。
classname = StringField() email = EmailField() recipient = Recipient(name='admin', email='root@localhost') recipient.save() # 会产生一个 ValidationError 错误 recipient.save(validate=False) # 不会
ListField将另一个字段对象作为其第一个参数,其中指定了列表中可能存储哪些类型的元素。
class Page(Document): tags = ListField(StringField(max_length=50))
参考字段可以使用ReferenceField存储到数据库中的其他文档。将另一个文档作为第一个参数传递给构造器,然后简单地将文档对象分配给字段:
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 = johnpost.save()
如果通过引用列表实现一对多关系,则引用将存储为DBRefs,并且需要将对象的实例传递给查询
classname = 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() # 查找所有由bob编写的网页,此处用到了高级查询,后面会介绍 Page.objects(authors__in=[bob]) # 查找所有由bob和john编写的网页 Page.objects(authors__all=[bob, john]) # 删除由bob编写的某篇文章(根据某个id查找到的). Page.objects(id='...').update_one(pull__authors=bob) # 把john添加到某篇文章(根据某个id查找到的). Page.objects(id='...').update_one(push__authors=john)
默认情况下,MongoDB不检查数据的完整性,因此删除其他文档仍然存在引用的文档将导致一致性问题。MongoEngine的ReferenceField增加了一些功能来防范这些类型的数据库完整性问题,为每个引用提供删除规则规范。通过reverse_delete_rule在ReferenceField定义上提供属性来指定删除规则,如下所示:
class... employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE) #这个例子中的声明意味着当一个Employee对象被移除时,ProfilePage引用该雇员的那个也被移除。如果删除了整批员工,则所有链接的配置文件页面也会被删除
- mongoengine.DO_NOTHING 这是默认设置,不会执行任何操作。删除速度很快,但可能会导致数据库不一致或悬空引用。
- mongoengine.DENY 如果仍存在对被删除对象的引用,则拒绝删除。
- mongoengine.NULLIFY 删除任何仍然指向被删除对象的对象字段(使用MongoDB的“未设置”操作),从而有效地消除关系。 即删除了被引用的字段,对引用它的字段无影响,举个例子,假如,文章的作者字段采用的是引用字段,那么作者一旦被删除,那么,由他写的文章仅仅是没有了作者,他的文章都还在。
- mongoengine.CASCADE 引用字段被删除,则引用此字段的文档也会被删除, 举个例子,假如,文章的作者字段采用的是引用字段,那么作者一旦被删除,那么,由他写的文章也都被删除。
- mongoengine.PULL 从ListField(ReferenceField)的任何对象的字段中删除对该对象的引用(使用MongoDB的“拉”操作 )。
第二种场景也存在,GenericReferenceField。这允许引用任何类型的Document,因此不会将Document子类作为构造函数参数:
classurl = 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() Note
可以在集合上指定索引以加快查询速度。这是通过创建indexes在meta字典中调用的索引规范列表完成的,其中索引规范可以是单个字段名称,包含多个字段名称的元组和包含完整索引定义的字典。 顺序可以通过在字段名前加上+(用于升序)或-号(用于降序)来指定。请注意,方向只对多字段索引很重要。文本索引可以通过在字段名前添加一个$来指定。散列索引可以通过在字段名前添加#来指定:
classcategory = IntField() title = StringField() rating = StringField() created = DateTimeField() meta = { 'indexes': [ 'title', '$title', # 文本索引 '#title', # 散列索引 ('title', '-rating'), ('category', '_cls'), { 'fields': ['created'], 'expireAfterSeconds': 3600 } ] }
如果采用字段,那么以下选项可用:
- fileds 要索引的字段。与上述相同的格式指定
- cls 如果您有多态模型可以继承并allow_inheritance打开,则可以配置索引是否应该将该_cls字段自动添加到索引的开头
- unique 索引是否应该是唯一的
- expireAfterSeconds(可选的) 允许您通过设置以秒为单位时间,过期来自动将某个字段中的数据过期。
可以使用QuerySet使用的ordering属性指定默认排序,排序在QuerySet创建时应用,并且可以通过随后的order_by()覆盖:
fromclass 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() # 通过默认的排序(降序),获取第一篇文章 # from BlogPost.meta.ordering latest_post = BlogPost.objects.first() assert latest_post.title == "Blog Post #3" # 覆盖原来的默认排序规则,采用升序 first_post = BlogPost.objects.order_by("+published_date").first() assert first_post.title == "Blog Post #1"
可以使用order_by()按1个或多个键确定结果。
# Order by ascending dateblogs = BlogPost.objects().order_by('date') # equivalent to .order_by('+date') # Order by ascending date first, then descending titleblogs = BlogPost.objects().order_by('+date', '-title')
# Only the first 5 peopleusers = User.objects[:5] # All except for the first 5 peopleusers = User.objects[5:] # 5 users, starting from the 11th user foundusers = User.objects[10:15]
查询可以通过调用带有字段查找关键字参数对象进行筛选。关键字参数中的密钥与您正在查询document上的字段对应:
# This will return a QuerySet that will only iterate over users whose# 'country' field is set to 'uk'uk_users = User.objects(country='uk')
嵌入式文档上的字段也可以使用字段查找语法,使用双下划线代替对象属性访问语法的点
# This will return a QuerySet that will only iterate over pages that have# been written by a user whose 'country' field is set to 'uk'uk_pages = Page.objects(author__country='uk')
class Page(Document): tags = ListField(StringField()) # This will match all pages that have the word 'coding' as an item in the# 'tags' listPage.objects(tags='coding')
Page.objects(tags__0='db')
# comments - skip 5, limit 10Page.objects.fields(slice__comments=[5, 10])
ost.objects(comments__by="joe").update(**{'inc__comments__$__votes': 1}) ost.objects(comments__by="joe").update(**{'inc__comments__$__votes': 1})
第一个参数是定义该方法的document类 第二个是初始查询集。该方法需要用queryset_manager()装饰才能被识别。
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来进行查询,需要在meta中进行配置
class AwesomerQuerySet(QuerySet): def get_awesome(self): return self.filter(awesome=True) class Page(Document): meta = {'queryset_class': AwesomerQuerySet} # To call:Page.objects.get_awesome()
num_users = User.objects.count()
yearly_expense = Employee.objects.sum('salary')
mean_age = User.objects.average('age')
MongoEngine提供了一个方法来获取一个在集合里item的频率-item_frequencies()。下面一个例子可以生成tag-clouds
class Article(Document): tag = ListField(StringField()) # After adding some tagged articles... tag_freqs = Article.objects.item_frequencies('tag', normalize=True) from operator import itemgetter top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10]
class Person(Document): name = StringField() Person(name='John').save() Person(name='Bob').save() pipeline = [ {"$sort" : {"name" : -1}}, {"$project": {"_id": 0, "name": {"$toUpper": "$name"}}} ]data = Person.objects().aggregate(pipeline) assert data == [{'name': 'BOB'}, {'name': 'JOHN'}]
和 或的组合操作
from mongoengine.queryset.visitor import Q # Get published postsPost.objects(Q(published=True) | Q(publish_date__lte=datetime.now())) # Get top posts Post.objects((Q(featured=True) & Q(hits__gte=1000)) | Q(hits__gte=5000))
- set–设置特定值
- unset–删除特定值(自蒙哥德布v1.3)
- inc–将价值增加一个给定金额
- dec–将价值减损给定金额
- push–将值附加到列表中
- push_all–将多个值附加到列表中
- pop–根据值删除列表的第一个或最后一个元素
- pull–从列表中删除值
- pull_all–从列表中删除多个值
- add_to_set–仅在其不在列表中的情况下向列表添加值
>>> 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']
如果不知道索引的位置,可以使用以下操作来更新列表项目
post = BlogPost(title='Test', page_views=0, tags=['database', 'mongo']) >>> post.save() >>> BlogPost.objects(id=post.id,tags='mongo').update(set__tags__S='mongodb') >>>post.reload() >>>post.tags ['database', 'mongodb']
本文作者:Eric
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!