一,说明
使用pymongo和mongoengine,就是基本的curd
参考链接:
pymongo 文档: https://www.mongodb.com/zh-cn/docs/languages/python/pymongo-driver/current/read/specify-a-query/
mongoengine 文档:https://docs.mongoengine.org/guide/querying.html
https://www.cnblogs.com/zhenyauntg/p/13201826.html
mongo 一对多数据 :https://cloud.tencent.com/developer/ask/sof/101750948
https://deepinout.com/mongoengine/mongoengine-questions/217_mongoengine_mongoengine_listfield_within_a_embeddeddocument_throws_typeerror_on_validation.html
二,代码
1,基类
class MongoDbModel(db.Document):
meta = {
'abstract': True,
'allow_inheritance': True
}
deleted_at = DateTimeField(default=None, null=True)
@classmethod
def connect(cls, db_name=None, username=None, password=None, host='mongodb://localhost:27017'):
"""链接数据库"""
if not db_name:
db_name = 'test'
if not username and not password:
db.connect(host=host, db=db_name, username=username, password=password)
else:
db.connect(host=host, db=db_name)
@classmethod
def disconnect(cls):
"""断开数据库"""
db.disconnect()
@classmethod
def mongo_add(cls, **kwargs):
return cls(**kwargs).save()
@classmethod
def find_one(cls, **kwargs):
return cls.find_many(**kwargs).first()
@classmethod
def find_many(cls, **kwargs):
kwargs['deleted_at'] = None
return cls.objects(**kwargs)
# return cls.objects.filter(**kwargs)
@classmethod
def find_add_paginate(cls, page, per_page, order_by=None, asc=True, **kwargs):
skip = (page - 1) * per_page
kwargs['deleted_at'] = None
total = cls.find_many(**kwargs).count()
total_pages = total // per_page + (1 if total % per_page > 0 else 0)
if order_by:
if asc:
items = cls.find_many(**kwargs).order_by(order_by).skip(skip).limit(per_page)
else:
items = cls.find_many(**kwargs).order_by(f'-{order_by}').skip(skip).limit(per_page)
else:
items = cls.find_many(**kwargs).skip(skip).limit(per_page)
return total, total_pages, items
@classmethod
def add_or_update_many(cls, objects):
cls_collection = cls._get_collection()
operations = []
for obj in objects:
if obj.id:
tmp_obj = cls.find_one(id=obj.id)
if tmp_obj is None:
raise NoneException(f'数据[{obj.id}]不存在')
operations.append(UpdateOne({'_id': obj.id}, {'$set': obj.to_mongo()}))
else:
operations.append(InsertOne(obj.to_mongo()))
if operations:
result = cls_collection.bulk_write(operations)
if result.inserted_count or result.matched_count:
return True
else:
return False
else:
return False
@classmethod
def delete_many_by_objects(cls, objects):
ids = [obj.id for obj in objects]
return cls.delete_many_by_ids(ids)
@classmethod
def delete_many_by_ids(cls, ids):
cls_collection = cls._get_collection()
# result = cls_collection.delete_many({'_id': {'$in': ids}})
# return result.deleted_count > 0
cls_collection.update_many({'_id': {'$in': ids}}, {'$set': {'deleted_at': datetime.datetime.now()}})
@classmethod
def delete_many_by_condition(cls, **kwargs):
cls_collection = cls._get_collection()
filter_dict = {}
for key, value in kwargs.items():
field, operator = key.split('__', 1)
if operator == 'in':
filter_dict[field] = {'$in': value}
elif operator == 'gt':
filter_dict[field] = {'$gt': value}
elif operator == 'eq':
filter_dict[field] = value
elif operator == 'gte':
filter_dict[field] = {'$gte': value}
elif operator == 'lt':
filter_dict[field] = {'$lt': value}
elif operator == 'lte':
filter_dict[field] = {'$lte': value}
elif operator == 'ne':
filter_dict[field] = {'$ne': value}
elif operator == 'like':
filter_dict[field] = {'$regex': f'^{value}$', '$options': 'i'}
elif operator == 'ilike':
filter_dict[field] = {'$regex': f'^{value}$', '$options': 'i'}
elif operator == 'notlike':
filter_dict[field] = {'$not': {'$regex': f'^{value}$', '$options': 'i'}}
elif operator == 'notilike':
filter_dict[field] = {'$not': {'$regex': f'^{value}$', '$options': 'i'}}
else:
filter_dict[key] = value
# result = cls_collection.delete_many(filter_dict)
# return result.deleted_count > 0
cls_collection.update_many(filter_dict, {'$set': {'deleted_at': datetime.datetime.now()}})
@classmethod
def delete_many_by_dict(cls, **kwargs):
cls.objects.filter(**kwargs).update(set__deleted_at=datetime.datetime.now())
def mongo_update(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
return self.save()
def mongo_delete(self):
self.deleted_at = datetime.datetime.now()
self.save()
return True
@classmethod
def mongo_drop(cls):
cls.objects.update(set__deleted_at=datetime.datetime.now())
# cls._get_collection().drop()
def mongo_close(self):
self.disconnect()
2,基本使用
继承基类
class User(MongoDbModel):
user_name = db.StringField(required=True)
age = db.IntField()
测试
def test_mongodb_user():
User.connect()
User.mongo_drop()
init_data = []
for i in range(50):
init_data.append(User(user_name=f'user{i}', age=i))
User.add_or_update_many(init_data)
users1 = User.find_one(user_name='user4')
User.delete_many_by_dict(user_name='user4')
# print(users1.to_json())
users1.mongo_delete()
print('\n')
pprint.pprint(users1)
users2 = User.find_many(age__gt=10)
print('\n')
pprint.pprint(users2)
# conditions = {'age__gt': 10, 'user_name__in': ['user11', 'user12']}
# users3 = User.find_add_paginate(page=1, per_page=10, **conditions)
users3 = User.find_add_paginate(page=1, per_page=10, age__gt=10)
users3 = User.find_add_paginate(page=1, per_page=10, order_by='age', asc=False, age__gt=10)
print('\n')
pprint.pprint(users3)
user0 = User.mongo_add(user_name='Tom', age=20)
user0.age = 21
user0.mongo_update(age=22)
user1 = User(user_name='John', age=25)
User.add_or_update_many([user0, user1])
user1 = User.find_one(user_name='John')
user2 = User.mongo_add(user_name='Mike', age=30)
# 批量按条件删除
User.delete_many_by_ids([user1.id, user2.id])
User.delete_many_by_objects([user0, user1, user2])
conditions = {'age__gt': 10, 'user_name__in': ['user11', 'user12']}
User.delete_many_by_condition(**conditions)
# User.delete_many_by_condition(age__gt=10)
User.mongo_drop()
User.disconnect()
3, 一对多
注意:
MongoEngine定义EmbeddedDocumentListField时,MongoEngine需要确保相关的EmbeddedDocument类已经被加载和注册。
如果将Student类放到Person类的后面,那么在解析Person类定义时,MongoEngine会尝试解析students字段的类型,但是它可能会发现Student类尚未被加载和注册,从而导致报错
也就是说,写类的时候,将子类,放到父类的上面
A, 子表数据,存储在主表
继承EmbeddedDocument的,不会在数据库中单独存储,而是作为一个字段存储在父文档中
1对多关系,子数据存储在父数据中,父数据通过ReferenceField来引用子数据。1张表
class Student(db.EmbeddedDocument):
# person_id = db.ReferenceField(Person)
student_name = db.StringField(required=True)
age = db.IntField()
class Person(MongoDbModel):
person_name = db.StringField(required=True)
age = db.IntField()
hobby = db.ListField(db.StringField())
students = db.EmbeddedDocumentListField(Student)
测试
def test_mongodb_person():
Person.connect()
Person.mongo_drop()
init_data = []
for i in range(50):
init_data.append(Person(person_name=f'person{i}', age=i, hobby=['reading', 'running'], students=[Student(student_name=f'student{i}', age=i), Student(student_name=f'student{i + 1}', age=i + 1)]))
Person.add_or_update_many(init_data)
persons3 = Person.find_add_paginate(page=1, per_page=10, age__gt=10)
Person.mongo_drop()
Person.disconnect()
B,子表数据,单独存储在新的一张表
class Dog(MongoDbModel):
name = db.StringField(required=True)
age = db.IntField()
animal = db.ReferenceField('Animal')
class Animal(MongoDbModel):
name = db.StringField(required=True)
age = db.IntField()
测试
def test_mongodb_animal():
Animal.connect()
Animal.mongo_drop()
client = Animal._get_db().client
with client.start_session() as mongo_session:
with mongo_session.start_transaction():
try:
for i in range(50):
animal = Animal(name=f'animal{i}', age=i, )
animal.save(session=mongo_session)
dog = Dog(name=f'dog{i}', age=i, animal=animal)
dog.save(session=mongo_session)
except Exception as e:
mongo_session.abort_transaction()
print(e.args[0])
finally:
mongo_session.end_session()
animal1 = Animal.find_one(age=11)
# 查询子表,需要反向关联
dogs = Dog.find_many(animal=animal1)
for dog in dogs:
print(dog.age)
4, 多对多
.....还没看。