MongoEngine不为人知的方法

之前发了个博客写的是MongoEngine的入门文档,在清楚了建表的字段类型,学会如何查询之后,我们来聊聊MongoEngine不常用但很有用的其他方法。


文章目录

  • MongoEngine不为人知的方法
  • 1.get查询容错
  • 2.with_id使用
  • 3.filter中字符查询-contains使用
  • 4.不包含查询
  • 5.list转querySet
  • 6.querySet转dict
  • 7.Serializer处理
  • 8. item_frequencies的使用
  • 9.scalar
  • 10.in_bulk
  • 11.唯一性约束
  • 12.跳过文档验证保存
  • 13.使用pymongo语法


1.get查询容错

# 查询某个用户时,get方法有则返回queryset,无则报错User.DoesNotExist
user = User.objects.get(name="xx")
# 为防止报错, 有则返回queryset,无则返回None
user = User.objects.filter(name="xx")
if user:
    user = user[0]
# 或者
user = User.objects.filter(name="xx").first()
# 进一步优化
user = User.objects(name="xx").first()

2.with_id使用

# mongo默认id类型为ObjectId,所以使用id查询时,需将str转换为ObjectId
from bson import ObjectId
user = User.objects.get(id=ObjectId(user_id))
# 优化
user = User.objects.with_id(user_id)

3.filter中字符查询-contains使用

# contains包含,icontains包含(忽略大小写)
# 模糊检索时对象属性包含所查询字符,如name为abc,输入ab
user = User.objects.filter(name__contains=search_str)

4.不包含查询

这个功能让我找了好久…知道怎么写的时候眼泪掉下来

# 包含contains 不包含not__contains
# 姓名不包含aa的人
user = User.objects.filter(name__not__contains="aa")

5.list转querySet

# 多对多查询时,查询到某对象关联的列表集,进一步使用filter时报错非querySet
set_role = Role.objects.filter(pk__in=[i.pk for i in role_list if i])

6.querySet转dict

user = User.objects.get(name="xxx")
# 需⚠️的是,若将此功能作为结果集的serializer使用,不应该包含外键关联字段
# 用fields方法过滤指定字段也不起作用
user_dict = user.to_mongo().to_dict()

7.Serializer处理

# 自定义函数
# 序列化处理,排除指定字段
def m2d_exclude(obj, *args):
    model_dict = obj.to_mongo().to_dict()
    if args:
        list(map(model_dict.pop, list(args)))
    if "_id" in model_dict.keys():
        model_dict["_id"] = str(model_dict["_id"])
    return model_dict

# 序列化处理,只返回特定字段
def m2d_fields(obj, *args):
    model_dict = obj.to_mongo().to_dict()
    if args:
        fields = [i for i in model_dict.keys() if i not in list(args)]
        list(map(model_dict.pop, fields))
    if "_id" in model_dict.keys():
        model_dict["_id"] = str(model_dict["_id"])
    return model_dict

调用

class Role(db.Document):
    name = db.StringField(verbose_name="角色名称")
    desc = db.StringField(verbose_name="职责描述")
    permission = db.ListField(db.ReferenceField(Permission, reverse_delete_rule=4), verbose_name="权限")
    staff = db.ListField(db.ReferenceField(Staff, reverse_delete_rule=4), verbose_name="关联员工")
    
    
role = Role.objects.get(name="管理员")
result = m2d_exclude(role, "permission", "staff")
# 或
result = m2d_fields(role, "name", "desc", "_id")


8. item_frequencies的使用

文档是这么写的:返回整个查询文档集中字段存在的所有项的字典及其对应的频率,即某字段所有值的集合(去重)和结果出现次数,简单来说就是group_by

想到的应用场景

  1. 一个班级所有人的数学成绩,不及格的多少人,60-70的多少人,80-90的多少人…
  2. 分类

使用:


9.scalar

获取所查询的字段值的列表


10.in_bulk

通过索引列表获取queryset

# 不使用in_bulk
# 通常情况,前端发送id列表
ids = data.json["ids"]
result = [Role.objects.with_id(i) for i in ids]
或
result = Role.objects(pk__in=ids)

# 使用in_bulk
ids = data.json("ids")
ids = [ObjectId(i) for i in ids]
documents = Role.objects.in_bulk(ids)
results = [documents.get(obj_id) for obj_id in ids]

注意: 列表生成式会导致list类型发生变化,无法继续filter

11.唯一性约束

MongoEngine允许你通过提供unique=True给Field构造函数来指定一个字段在集合中是唯一的。如果您尝试将与唯一字段具有相同值的文档保存为数据库的文档, NotUniqueError则会引发。您也可以使用下列方法指定多字段唯一性约束:unique_with可以是单个字段名称,也可以是字段名称的列表或`元组

class User(Document):
    username = StringField(unique=True)
    first_name = StringField()
    last_name = StringField(unique_with='first_name')

12.跳过文档验证保存

您也可以通过设置validate=False调用save() 方法时跳过整个文档验证过程

class Recipient(Document):
    name = StringField()
    email = EmailField()

recipient = Recipient(name='admin', email='root@localhost')
recipient.save()               # 会产生一个 ValidationError 错误
recipient.save(validate=False) # 不会

2019-05-06 更新

13.使用pymongo语法

使用名称Model Model作为您在实例中为连接定义的实际类的占位符:

Model._get_collection().aggregate([
    { '$group' : 
        { '_id' : { 'carrier' : '$carrierA', 'category' : '$category' }, 
          'count' : { '$sum' : 1 }
        }
    }
])

所以你可以随时访问pymongo对象而不建立单独的连接. Mongoengine本身建立在pymongo上.
示例

  1. 标签数量大于3的学生
class Tag(documents):
  name = StringField()
  
class Student(documents):
  name = StringField()
  tag = ListField(ReferenceField(Tag))

# 使用原生查询
db.student.find({ "tag.3" : { "$exists" : 1 } })
# ORM查询
Student.objects(__raw__={ "tag.3":{ "$exists":1}})
  1. 姓名相同的学生数量
# 原生mongo
db.getCollection("student").aggregate([
	{"$match":{"status":0}},
	{"$sortByCount":"$name"},
	{"$match":{"count":{"$gt":1}}}
]).itcount()

# ORM
a = Student._get_collection().aggregate([
	{"$match":{"status":0}},
	{"$sortByCount":"$name"},
	{"$match":{"count":{"$gt":1}}}
])
l = list(a)
len(l)