与Django一样,Document类都有一个 objects 属性。它用于将类与数据库关联起来。objects属性是一个QuerySetManager类型的对象,它的操作会返回一个QuerySet类型的对象。可以通过对QuerySet对象的迭代获取数据库中的数据。

class User(Document):
    name = StringField()
    country = StringField()

class Paper(Document):
    content = StringField()
    author = ReferenceField(User) #这样写是一对一,如果想一对多的话应该这样写 author = listField(ReferenceField(User))

增加

user = User(email='223qq.com').save() #不知道pycharm为什么会显示语法错误
u1 = User.objects.filter(name='1').first()
u2  = User.objects.fileter(name='1').fitst()

page = Page(cotent='美丽的中国',author=[u1,u2].save()

 

查询

可以在查询是指定过滤条件以获取想要的结果。例如想要查询英国的用户:

uk_users = User.objects(country='uk')

查询条件:

与Django类似,MongoEngine同样也提供了一些条件语句。

ne - 不相等
lt - 小于
lte - 小于等于
gt - 大于
gte - 大于等于
not - 取反
in - 值在列表中
nin - 值不在列表中
mod - 取模
all - 与列表的值相同
size - 数组的大小
exists - 字段的值存在

例如查询年龄小于等于18岁的用户:

young_users = Users.objects(age__lte=18)
对于不同类型的数据提供了不同的条件语句。

查询结果个数限制
跟传统的ORM一样,MongoEngine也可以限制查询结果的个数。一种方法是在QuerySet对象上调用limit和skip方法;另一种方法是使用数组的分片的语法。例如:

users = User.objects[10:15]
users = User.objects.skip(10).limit(5)

嵌入文档查询

查询列表字段:

这个是在项目找那个遇到的,同事写的当时很费解,为什么有这种操作

还是举个例子吧:

# model

class User(Document):
    name = StringField()
    age = StringField()


class Login_token(EmbeddedDocument):
    value = StringField()


class Session(Document):
    users = ListField(LazyReferenceField(User))
    login_tokens = ListField(EmbeddedDocumentField(Login_token))

 

数据库数据库数据

# session

{
    "_id" : ObjectId("5da0679f23641b6d95c09ef6"),
    "users" : [ 
        ObjectId("5da0664b23641b6cc5500b63"), 
        ObjectId("5da0663a23641b6cb946e986")
    ],
    "login_tokens" : [ 
        {
            "value" : "1"
        }, 
        {
            "value" : "2"
        }
    ]
}


##user
{
    "_id" : ObjectId("5da0663a23641b6cb946e986"),
    "name" : "小红"
}

/* 2 */
{
    "_id" : ObjectId("5da0664b23641b6cc5500b63"),
    "name" : "小李"
}
session集合数据

 

题目要求:

查找value=1且name等于小红的session

ession = Session.objects.filter(login_tokens__value="1", users="5da0663a23641b6cb946e986")
print(session)

结果:

[<Session: Session object>]

  

还有个例子

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') #他将查询列表中tags列表中包含coding的文档

还可以按照列表中的位置来查询

Page.objects(tags__0='db')

连表查询

在关系型数据库中我们用经常用连表查询,但是在MongoDB中,我们必须借助aggragate来实现连表,详细见下面的aggragate模块

元查询

有的程序员喜欢用pymongo原生的noSQL语句,在这里我们可以用__raw__来实现

It is possible to provide a raw PyMongo query as a query parameter, which will be integrated directly into the query. This is done using the __raw__ keyword argument:

Page.objects(__raw__={'tags': 'coding'})

自定义查询

mongo中还支持自定义查询

class AwesomerQuerySet(QuerySet):

    def get_awesome(self):
        return self.filter(awesome=True)

class Page(Document):
    meta = {'queryset_class': AwesomerQuerySet} #在meta中自定义查询的类

# To call:
Page.objects.get_awesome()

引用

引用这个知识点我是在学了mongoengin好长时间才真正的理解会运用,具体来看实例

# model
class OperationLogging(db.DynamicDocument):
    """操作日志"""
    operate_time = db.IntField(default=now_timestamp)
    operate_obj = db.StringField()
    operator = db.LazyReferenceField('Users')
    shop = db.LazyReferenceField('Shops')
    operate_type = db.StringField()
    operate_result = db.StringField()

class Machines(db.DynamicDocument):
    """售货机基类"""
    imei = db.StringField()
    type = db.IntField()
    shop = db.LazyReferenceField('Shops')
    status = db.IntField()
    note = db.StringField()
    creator = db.LazyReferenceField('Users')

    class Pipeline(db.EmbeddedDocument):
        """售货机货道(即每个格子)"""
        id = db.IntField()
        product = db.LazyReferenceField('Products')
        stock = db.IntField()
        max_stock = db.IntField()

    pipelines = db.ListField(db.EmbeddedDocumentField(Pipeline))

这里有两个数据库模型类

题目要求我们已经得到一个机器对象即

machine_obj = Machines.objects(id=machine_id, deleted=False).first()

现在我们要添加操作日志记录

obj = OperationLogging.objects.create(operate_obj=operate_obj, operator=operator, shop=?,operate_type=operate_type,operate_result=operate_result)

操作日志中的shop我们应该怎么获取?显然我们已经知道通过machine_obj可以得到shop,具体有两种方式都可以实现

#方式一 machine_obj.shop 得到的是一个 <class 'mongoengine.base.datastructures.LazyReference'> 这个可以直接放在LazyReferenceField对应的值上
obj = OperationLogging.objects.create(operate_obj=operate_obj, operator=operator, shop=machine_obj.shop,
                                                  operate_type=operate_type,
                                                  operate_result=operate_result)
#方式二:machine_obj.shop.fetch() 得到的就是一个<class 'models.Shops'>,这个类对象可以当做引用对象直接用,可以放在LazyReferenceField对应的值上
obj = OperationLogging.objects.create(operate_obj=operate_obj, operator=operator, shop=machine_obj.shop.fetch(),
                                                  operate_type=operate_type,
                                                  operate_result=operate_result)

其实还有第三种方法:前提是你得到shop的具体对象时(object)

#方法三 :直接一个shop对象也可以放在LazyReferenceField对应的值上
shop_obj = Shops.objects.filter(id=1111,deleted).first()
obj = OperationLogging.objects.create(operate_obj=operate_obj, operator=operator, shop=shop_obj,
                                                  operate_type=operate_type,
                                                  operate_result=operate_result)

注意:一个引用可以用fetch()得到这个对象并点出相应的属性

Aggregation聚合查询

MongoEngine提供了一些数据库的聚合操作。在文章MongoDB基础中也有这个方法的介绍.地址:https://www.cnblogs.com/sticker0726/articles/11121745.html#_label5

注意:在经过aggregate查询后得到的是一个迭代器.含有__iter__和next方法

<class 'pymongo.command_cursor.CommandCursor'>

 

统计结果个数即可以使用QuerySet的count方法,也可以使用Python风格的方法:

num_users = len(User.objects)
num_users = User.objects.count()
其他的一些聚合操作。
求和:

yearly_expense = Employee.objects.sum('salary')
求平均数:

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

连表查询例题

#查询作者为中国的page
#在Django的orm中我们我可以直接这样查
uk_papers = Paper.objects(author__country='中国')

#但是在mongoengine中我们不可以这么查要这样=查

pipeline = [
    {'$lookup': {
        'from': "user1",
        'localField': "author",
        'foreignField': '_id',
        'as': 'authors'
    }},
    {'$match': {'authors.country': "中国"}}  # 这里一定要用我们在as属性中设置的属性值,不要用author
]
o = Paper1.objects.aggregate(*pipeline)
for i in o:
    print(i)

结果为:

{'_id': ObjectId('5db1090c23641b35a04cfe45'), 'content': '美丽的中国', 'author': ObjectId('5db108c923641b353c6d8340'), 'authors': [{'_id': ObjectId('5db108c923641b353c6d8340'), 'name': '1', 'country': '中国'}]} ####注意authors的属性值是列表

 

高级查询

有时需要将多个条件进行组合,比如或查询,在mongoengine中没有or,这个语法,前面提到的方法就不能满足需求了。这时可以使用MongoEngine的Q类。它可以将多个查询条件进行 &(与) 和 |(或) 操作。

例如下面的语句是查询所有年龄大于等于18岁的英国用户,或者所有年龄大于等于20岁的用户。

User.objects((Q(country='uk') & Q(age__gte=18)) | Q(age__gte=20))

在项目中遇到的情况,对于订单列表来说,销售员只能看到自己及下属的订单,所以查询语句为:

staff_query = Q(user_role__lt=g.current_login_user.role) | Q(user=ObjectId(g.current_login_user.pk))
# 查询
orders_qs = Orders.objects(staff_query).filter(**query)#**query中有一些条件,比如店铺id

 

 更新

MongoDB 文档 可以通过QuerySet 上的 update_one()update()modify() 方法自动更新。下面几种操作符可以被用到这几种方法上:


#几种操作符
set – 设置成一个指定的值

unset – 删除一个指定的值

inc – 将值加上一个给定的数字

dec – 将值减去一个给定的数字

push – 在 list 中添加一个值

push_all – 在 list 中添加几个值

pop – 移除list 的第一项或最后一项(根据 pop__<field>=val 中val 的值决定删除第一项还是最后一项,一般情况下,val 为负则删除第一项,为正则删除最后一项,参见:mongodb $pop

pull – 从 list 里面移除一个值

pull_all – 从 list 里面移除几个值

add_to_set – 当要添加的值不在 list 中时,添加这个值

 

DatedPage.objects(title='你好').update(title='大家好')  # 操作成功返回0
DatedPage.objects(title='你好1').update_one(set__title='红楼梦')
>>> 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')

 

 

 

 

posted on 2019-08-25 22:15  程序员一学徒  阅读(447)  评论(0)    收藏  举报