mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
style: 统一代码风格和格式 docs: 完善函数和方法的文档字符串 refactor(base_model): 移除冗余的表名和表参数生成方法 refactor(constant): 更新返回码注释格式 refactor(router_class): 添加路由处理器的详细文档 refactor(database): 完善数据库连接函数的文档 refactor(security): 添加认证类和方法的详细文档 refactor(validator): 更新验证器函数的文档格式 refactor(serialize): 优化序列化工具类的文档 refactor(response): 完善响应类的文档字符串 refactor(dependencies): 添加依赖函数的详细文档 refactor(initialize): 完善初始化脚本的文档 refactor(plugin): 添加生命周期和中间件注册的文档 refactor(service): 完善服务层方法的文档 refactor(controller): 添加控制器方法的详细文档 refactor(crud): 完善CRUD操作的文档字符串 refactor(schema): 简化模型类并移除冗余字段 refactor(param): 更新查询参数类的注释格式 refactor(template): 优化代码生成模板的格式 refactor(console): 添加控制台输出功能的实现 refactor(util): 完善工具函数的文档字符串
303 lines
10 KiB
Python
303 lines
10 KiB
Python
# mongo_curd.py
|
|
import datetime
|
|
import json
|
|
from typing import Any, List, Optional, Dict, Union
|
|
from bson import ObjectId
|
|
from bson.errors import InvalidId
|
|
from bson.json_util import dumps
|
|
from fastapi.encoders import jsonable_encoder
|
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
|
from pymongo.results import InsertOneResult, UpdateResult, DeleteResult
|
|
|
|
from app.core.exceptions import CustomException
|
|
|
|
class MongoCURD:
|
|
"""
|
|
MongoDB 数据库管理器
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
db: AsyncIOMotorDatabase,
|
|
collection: str,
|
|
schema: Any = None
|
|
):
|
|
"""
|
|
初始化 MongoDB CURD 类。
|
|
|
|
参数:
|
|
- db (AsyncIOMotorDatabase): 数据库连接。
|
|
- collection (str): 集合名称。
|
|
- schema (Any | None): 序列化对象,传入模型以进行编码,默认 None。
|
|
|
|
返回:
|
|
- None
|
|
"""
|
|
self.db = db
|
|
self.collection = db[collection]
|
|
self.schema = schema
|
|
|
|
async def get(self, _id: Optional[str] = None, **kwargs) -> Optional[Dict]:
|
|
"""
|
|
获取单个数据,默认使用 ID 查询,否则使用关键词查询。
|
|
|
|
参数:
|
|
- _id (str | None): 数据 ID,若提供则按 ID 查询。
|
|
- kwargs (Dict[str, Any]): 查询条件键值对。
|
|
|
|
返回:
|
|
- Dict | None: 查询到的数据字典,未找到返回 None。
|
|
|
|
异常:
|
|
- CustomException: 当 ID 无效或查询发生错误时抛出。
|
|
"""
|
|
try:
|
|
if _id:
|
|
kwargs["_id"] = ObjectId(_id)
|
|
params = self.filter_condition(**kwargs)
|
|
data = await self.collection.find_one(params)
|
|
if not data:
|
|
return None
|
|
return jsonable_encoder(self.schema(**data)) if self.schema else data
|
|
except InvalidId:
|
|
raise CustomException(msg="无效的ID格式")
|
|
except Exception as e:
|
|
raise CustomException(msg=f"查询数据失败: {str(e)}")
|
|
|
|
async def create(self, data: Union[Dict, Any]) -> InsertOneResult:
|
|
"""
|
|
创建数据。
|
|
|
|
参数:
|
|
- data (Dict | Any): 要创建的数据,可为字典或可编码对象。
|
|
|
|
返回:
|
|
- InsertOneResult: 插入操作结果。
|
|
|
|
异常:
|
|
- CustomException: 创建失败时抛出。
|
|
"""
|
|
try:
|
|
if not isinstance(data, dict):
|
|
data = jsonable_encoder(data)
|
|
|
|
# 添加时间戳
|
|
now = datetime.datetime.now()
|
|
data.update({
|
|
'created_at': now,
|
|
'updated_at': now
|
|
})
|
|
|
|
result = await self.collection.insert_one(data)
|
|
if not result.acknowledged:
|
|
raise CustomException(msg="创建数据失败")
|
|
return result
|
|
except Exception as e:
|
|
raise CustomException(msg=f"创建数据失败: {str(e)}")
|
|
|
|
async def update(self, _id: str, data: Union[Dict, Any], upsert: bool = False) -> UpdateResult:
|
|
"""
|
|
更新数据。
|
|
|
|
参数:
|
|
- _id (str): 数据 ID。
|
|
- data (Dict | Any): 要更新的数据,可为字典或可编码对象。
|
|
- upsert (bool): 不存在是否插入,默认 False。
|
|
|
|
返回:
|
|
- UpdateResult: 更新操作结果。
|
|
|
|
异常:
|
|
- CustomException: ID 无效或更新失败时抛出。
|
|
"""
|
|
try:
|
|
if not isinstance(data, dict):
|
|
data = jsonable_encoder(data)
|
|
|
|
# 更新时间戳
|
|
data['updated_at'] = datetime.datetime.now()
|
|
|
|
result = await self.collection.update_one(
|
|
{'_id': ObjectId(_id)},
|
|
{'$set': data},
|
|
upsert=upsert
|
|
)
|
|
if result.matched_count == 0 and not upsert:
|
|
raise CustomException(msg="更新失败,未找到对应数据")
|
|
return result
|
|
except InvalidId:
|
|
raise CustomException(msg="无效的ID格式")
|
|
except Exception as e:
|
|
raise CustomException(msg=f"更新数据失败: {str(e)}")
|
|
|
|
async def delete(self, _id: Union[str, List[str]]) -> DeleteResult:
|
|
"""
|
|
删除数据,支持批量删除。
|
|
|
|
参数:
|
|
- _id (str | List[str]): 单个 ID 或 ID 列表。
|
|
|
|
返回:
|
|
- DeleteResult: 删除操作结果。
|
|
|
|
异常:
|
|
- CustomException: ID 无效或未删除任何数据时抛出。
|
|
"""
|
|
try:
|
|
if isinstance(_id, list):
|
|
result = await self.collection.delete_many({'_id': {'$in': [ObjectId(i) for i in _id]}})
|
|
else:
|
|
result = await self.collection.delete_one({'_id': ObjectId(_id)})
|
|
|
|
if result.deleted_count == 0:
|
|
raise CustomException(msg="删除失败,未找到对应数据")
|
|
return result
|
|
except InvalidId:
|
|
raise CustomException(msg="无效的ID格式")
|
|
except Exception as e:
|
|
raise CustomException(msg=f"删除数据失败: {str(e)}")
|
|
|
|
async def list(
|
|
self,
|
|
page_no: Optional[int] = 1,
|
|
page_size: Optional[int] = 10,
|
|
order_by: Optional[List[Dict]] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""
|
|
查询数据列表。
|
|
|
|
参数:
|
|
- page_no (int | None): 页码,默认 1。
|
|
- page_size (int | None): 每页数量,默认 10。
|
|
- order_by (List[Dict] | None): 排序条件,形如 [{'field': '字段名', 'direction': 1}]。
|
|
- kwargs (Dict[str, Any]): 查询条件键值对。
|
|
|
|
返回:
|
|
- List[Dict]: 数据列表。
|
|
|
|
异常:
|
|
- CustomException: 查询失败时抛出。
|
|
"""
|
|
try:
|
|
params = self.filter_condition(**kwargs)
|
|
cursor = self.collection.find(params)
|
|
|
|
# 排序处理
|
|
if order_by:
|
|
sort_conditions = [(item['field'], item['direction']) for item in order_by]
|
|
cursor.sort(sort_conditions)
|
|
|
|
# 分页处理
|
|
if page_no and page_size:
|
|
cursor.skip((page_no - 1) * page_size).limit(page_size)
|
|
|
|
data_list = [json.loads(dumps(row)) async for row in cursor]
|
|
return [jsonable_encoder(self.schema(**data)) for data in data_list] if self.schema else data_list
|
|
except Exception as e:
|
|
raise CustomException(msg=f"查询列表失败: {str(e)}")
|
|
|
|
async def count(self, **kwargs) -> int:
|
|
"""
|
|
获取数据总数。
|
|
|
|
参数:
|
|
- kwargs (Dict[str, Any]): 查询条件键值对。
|
|
|
|
返回:
|
|
- int: 数据总数。
|
|
|
|
异常:
|
|
- CustomException: 统计失败时抛出。
|
|
"""
|
|
try:
|
|
params = self.filter_condition(**kwargs)
|
|
return await self.collection.count_documents(params)
|
|
except Exception as e:
|
|
raise CustomException(msg=f"统计数据失败: {str(e)}")
|
|
|
|
@staticmethod
|
|
def filter_condition(**kwargs) -> Dict:
|
|
"""
|
|
构建过滤条件。
|
|
|
|
参数:
|
|
- kwargs (Dict[str, Any]): 查询参数,支持 ('like'|'between'|'ObjectId'|'in'|'gt'|'gte'|'lt'|'lte') 等操作。
|
|
|
|
返回:
|
|
- Dict: 过滤条件字典。
|
|
|
|
异常:
|
|
- CustomException: 当 ObjectId 格式无效时抛出。
|
|
"""
|
|
params = {}
|
|
for k, v in kwargs.items():
|
|
if not v:
|
|
continue
|
|
|
|
if isinstance(v, tuple):
|
|
if v[0] == "like" and v[1]:
|
|
params[k] = {'$regex': v[1], '$options': 'i'} # i表示不区分大小写
|
|
elif v[0] == "between" and len(v[1]) == 2:
|
|
params[k] = {
|
|
'$gte': f"{v[1][0]} 00:00:00",
|
|
'$lt': f"{v[1][1]} 23:59:59"
|
|
}
|
|
elif v[0] == "ObjectId" and v[1]:
|
|
try:
|
|
params[k] = ObjectId(v[1])
|
|
except InvalidId:
|
|
raise CustomException(msg="无效的ObjectId格式")
|
|
elif v[0] == "in" and v[1]:
|
|
params[k] = {'$in': v[1]}
|
|
elif v[0] == "gt":
|
|
params[k] = {'$gt': v[1]}
|
|
elif v[0] == "gte":
|
|
params[k] = {'$gte': v[1]}
|
|
elif v[0] == "lt":
|
|
params[k] = {'$lt': v[1]}
|
|
elif v[0] == "lte":
|
|
params[k] = {'$lte': v[1]}
|
|
else:
|
|
params[k] = v
|
|
|
|
return params
|
|
|
|
# from app.api.v1.module_system.log.schema import OperationLogOutSchema
|
|
|
|
# class OperationRecordDal(MongoCURD):
|
|
# """
|
|
# 操作记录数据访问层
|
|
# """
|
|
|
|
# def __init__(self, db: AsyncIOMotorDatabase):
|
|
# """
|
|
# 初始化操作记录数据访问层。
|
|
|
|
# :param db: 数据库连接
|
|
# """
|
|
# super().__init__(
|
|
# db=db,
|
|
# collection="system_operation_log",
|
|
# schema=OperationLogOutSchema,
|
|
# )
|
|
|
|
# 创建日志到mongodb(已测试成功,可以成功创建):暂时注释,是因为该中间保存日志到mongodb(已调试成功),而我现在实现的是记录到mysql的log表
|
|
# if not settings.MONGO_DB_ENABLE:
|
|
# return response
|
|
# document = OperationLogCreateSchema(
|
|
# request_ip = request.client.host,
|
|
# request_os = user_agent.os.family,
|
|
# request_browser = user_agent.browser.family,
|
|
# request_path = request.url.path,
|
|
# request_method = request.method,
|
|
# request_payload = oper_param,
|
|
# response_code = response.status_code,
|
|
# response_json = response_data.decode(),
|
|
# description = route.name,
|
|
# creator_id = creator_id
|
|
# )
|
|
# from app.core.mongo_curd import OperationRecordDal
|
|
# from app.core.dependencies import mongo_getter
|
|
# operation_record_dal = OperationRecordDal(db = await mongo_getter(request))
|
|
# await operation_record_dal.create(data=document.model_dump()) |