目录
③第三个函数:get_asset_by_pid(pid: int)(注意这里的函数名和实际参数名不一致)
以下是对D:\xbill-server\app\api\mb\asset.py资产界面的理解:
一、引入
from fastapi import APIRouter, HTTPException
from app.db.models import MbAsset
from pydantic import BaseModel, Field
from typing import Union, Optional
from utility import func
# 定义一个变量,用作API路由的标签
name = "资产类别"
# 创建一个API路由器实例
router = APIRouter(
prefix="/asset", # 所有路由都将以/asset作为前缀
tags=[name], # 将所有路由分组到名为"资产类别"的标签下
responses={404: {"description": "Not found"}}, # 定义全局404响应描述
)
-
prefix="/asset"
: 这个参数指定了所有通过这个路由器注册的路由都将以/asset
作为前缀。这意味着,如果我们在这个路由器下注册了一个路由/list
,那么实际的URL路径将会是/asset/list
。 -
tags=[name]
:tags
参数允许我们为通过这个路由器注册的所有路由指定一个或多个标签。这些标签在API文档中非常有用,因为它们可以帮助用户将相关的API端点分组。 -
responses={404: {"description": "Not found"}}
: 这个参数定义了一个全局响应,它将应用于通过这个路由器注册的所有路由。具体来说,它指定了当请求的资源不存在时(即HTTP 404状态码),应该返回的响应体。在这个例子中,响应体包含一个简单的描述:“Not found”。
下面,我们就可以使用这个路由器,注册多个与资产相关的API端点:
二、基于动态SQL查询的资产选择API接口
@router.post("/s")
async def Select(sql:str):
# 函数开始执行
# 假设MbAsset是一个可以执行数据库查询的对象
# 这里调用它的get_by_query方法,并传入一个动态构建的SQL查询字符串
# 注意:这里的"select "+sql是将字符串"select "和函数参数sql拼接起来
# 这样做是非常危险的,因为它允许任何人通过POST请求发送任意的SQL语句,可能导致SQL注入攻击
res = await MbAsset.get_by_query("select "+sql)
# get_by_query方法是一个异步方法,它执行查询并返回结果
# await关键字用于等待这个异步方法完成,并将结果赋值给变量res
# 函数返回一个字典,包含三个字段
# "code": 0 表示操作成功(或没有错误)
# "msg": '' 表示没有额外的消息(这个字段可能在出现错误时用于显示错误信息)
# "result": res 包含查询的结果
return {
"code": 0,
"msg": '',
"result":res
}
这段代码是FastAPI框架中的一个路由处理器,它定义了一个处理POST请求的路由。这个路由的URL路径是/s
。
通俗的总结就是,这段代码定义了一个FastAPI的路由,当客户端向/s
路径发送POST请求时,服务器会执行Select
函数。这个函数接收一个字符串参数sql
,然后将这个字符串拼接到"select "
后面,形成一个完整的SQL查询语句。接着,它调用MbAsset
对象的get_by_query
异步方法来执行这个查询,并等待查询结果。最后,它将查询结果、一个表示成功的状态码(0)和一个空的消息字段打包成一个字典返回给客户端。
三、资产查询API:全量、ID及PID查询
@router.get("/", summary="查询"+name, description="查询"+name, responses=func.Responses("查询"+name,RES))
async def get_assets():
res = await MbAsset.filter().all()
return {
"code": 0,
"msg": '',
"result":res
}
这段代码定义了一个FastAPI的路由,当用户访问网站的根地址时,会触发一个异步函数get_assets
的执行。这个函数会从某个地方(可能是数据库)获取所有的资产信息,并将这些信息打包成一个格式化的字典返回给客户端。
可以这样来通俗理解:
1.路由解释
想象一下你有一个网站(或者说是一个应用),这个网站上有不同的页面,每个页面都有一个地址(URL)。在FastAPI中,你定义了这些页面和它们对应的处理函数之间的关系,这叫做“路由”。
@router.get("/", ...)
这行代码告诉FastAPI:“嘿,当用户访问这个网站的根地址(/
,也就是网站的首页)时,你应该调用下面这个叫做get_assets
的函数来处理请求。”- 但是,这里的
...
代表了一些额外的信息,比如这个路由的摘要(summary)、描述(description)和响应(responses)。在实际应用中,这些信息会被用来生成API文档,帮助开发者了解如何使用这个接口。
2.异步函数解释
async def get_assets():
这行代码定义了一个异步函数。在Python中,异步函数允许你编写可以暂停执行的代码,而不会阻塞整个程序。这对于需要等待的操作特别有用,比如从数据库读取数据、从网络获取资源等。
在get_assets
函数中:
res = await MbAsset.filter().all()
这行代码调用了MbAsset
(可能是一个数据库模型或查询构建器)的filter().all()
方法。这个方法可能是用来从数据库中检索所有资产的。但是,因为它是异步的,所以await
关键字会暂停get_assets
函数的执行,直到all()
方法完成并返回结果。- 一旦
all()
方法返回了结果(赋值给res
),get_assets
函数就会继续执行,并将结果打包成一个字典返回给客户端。这个字典包含了三个字段:"code"
(表示操作的状态码,这里为0表示成功)、"msg"
(通常用于显示错误消息,这里为空字符串表示没有错误)、"result"
(包含了从数据库中检索到的资产数据)。
3.下面两段代码与该段代码的联系与区别为:
@router.get("/{id}", summary="查询"+name, description="根据ID查询"+name, responses=func.Responses("查询"+name,RES))
async def get_asset(id: int):
res = await MbAsset.filter(id=id).first()
return {
"code": 0,
"msg": '',
"result":res
}
# Add endpoint for getting a asset by pid
@router.get("/pid/{pid}", summary="查询"+name, description="根据PID查询"+name, responses=func.Responses("查询"+name,RES))
async def get_asset_by_pid(id: int):
res = await MbAsset.filter(pid=id)
return {
"code": 0,
"msg": '',
"result":res
}
①第一个函数:get_assets()
- URL路径:
/
- 功能:查询所有资产。
- 参数:无。
- 操作:通过调用
MbAsset.filter().all()
(这里假设MbAsset
是一个数据库模型或数据访问对象,它支持异步查询),查询所有资产记录。filter()
方法在这里可能实际上并没有过滤条件,因为它后面直接跟了.all()
来获取所有记录。 - 返回值:一个字典,包含状态码(
code
为0通常表示成功)、消息(msg
为空字符串,因为没有特别的消息要返回)和查询结果(result
)。
②第二个函数:get_asset(id: int)
- URL路径:
/{id}
- 功能:根据资产的ID查询单个资产。
- 参数:
id
(整数类型),指定要查询的资产的ID。 - 操作:通过调用
MbAsset.filter(id=id).first()
,根据提供的ID查询单个资产记录。.first()
方法用于获取查询结果中的第一条记录(如果有的话)。 - 返回值:与
get_assets()
相同格式的字典,但result
字段包含的是单个资产的数据(如果找到的话)。
③第三个函数:get_asset_by_pid(pid: int)
(注意这里的函数名和实际参数名不一致)
- URL路径:
/pid/{pid}
- 功能:根据资产的PID(可能是某种特定的标识符,不同于ID)查询资产。
- 参数:
pid
(整数类型),尽管函数定义中错误地使用了id
作为参数名,但URL路径和函数描述都正确指出了它是pid
。 - 操作:通过调用
MbAsset.filter(pid=id)
(注意这里的id
应该是pid
,这可能是一个错误),根据提供的PID查询资产记录。这里应该使用.all()
或类似方法来获取所有匹配的记录,因为.filter()
本身不会执行查询,它只是设置查询条件。但基于上下文,可能这里的意思是获取第一个匹配的记录,然而代码没有直接这样做。 - 返回值:与前面相同的字典格式,但
result
字段包含的是根据PID查询到的资产数据(可能是一个列表,如果查询返回多条记录的话,尽管通常这里可能期望只返回一个记录)。
④联系
-
目的相似:所有函数的目的都是查询资产信息,并将查询结果以一定的格式返回给客户端。
-
返回格式相同:所有函数都返回一个包含
code
、msg
和result
字段的字典。其中,code
用于表示操作状态(通常0表示成功),msg
用于提供额外的消息(在这段代码中为空字符串),result
用于包含查询结果。 -
使用异步:所有函数都使用了
async def
定义,表示它们是异步函数,适用于处理I/O密集型任务,如数据库查询。 -
可能的数据模型:虽然在这段代码中没有显式展示,但
MbAsset
很可能是一个数据模型或数据库访问对象,用于与数据库交互并执行查询。
⑤区别
- URL路径:
- 第一个函数使用
/
路径,表示查询所有资产。 - 第二个函数使用
/{id}
路径,表示根据资产的ID查询单个资产。 - 第三个函数(尽管参数命名有误)应该使用
/pid/{pid}
路径,表示根据资产的PID查询资产。这里的PID可能是另一种标识符,用于区分不同的资产。
- 第一个函数使用
- 参数:
- 第一个函数没有参数。
- 第二个函数有一个名为
id
的参数,表示要查询的资产的ID。 - 第三个函数(按照其URL路径和描述,尽管实现有误)应该有一个名为
pid
的参数,表示要查询的资产的PID。但实际上,代码中错误地使用了id
作为参数名。
- 查询操作:
- 第一个函数使用
MbAsset.filter().all()
查询所有资产。 - 第二个函数使用
MbAsset.filter(id=id).first()
根据ID查询单个资产。 - 第三个函数(修正参数名后)应该使用类似
MbAsset.filter(pid=pid).first()
或.all()
(取决于是否期望返回多个结果)的查询来根据PID查询资产。但当前代码中使用了错误的参数名id
,并且没有明确指出是期望单个结果还是多个结果。
- 第一个函数使用
- 用途和场景:
- 第一个函数适用于需要获取所有资产信息的场景。
- 第二个函数适用于需要根据特定ID获取单个资产信息的场景。
- 第三个函数(在修正后)适用于需要根据特定PID获取资产信息的场景,PID可能是另一种独特的标识符,用于区分资产。
四、资产管理API:新增、修改与删除操作
# @router.post("/", response_model=AssetItem)
@router.post("/", summary="新增"+name, description="添加新"+name, responses=func.Responses("新增"+name,1))
async def add_asset(item: AssetItem):
# print(item)
res = await MbAsset.create(name=item.name,pid=item.pid,iconPath=item.iconPath,amount=item.amount,sort=item.sort)
# res = await MbAsset.create(name='现金账户1',iconPath='icon-zhanghuyue1')
# print(dict(res))
return {
"code": 0,
"msg": '',
"result":res
}
@router.post("/edit/{id}/{name}/{iconPath}", summary="修改"+name, description="根据ID查询修改"+name, responses=func.Responses("修改"+name,RES))
async def update_asset(id: int, name: str, iconPath: str):
res = await MbAsset.filter(id=id).update(name=name,iconPath=iconPath)
return {
"code": 0,
"msg": '',
"result":{"name":name, "iconPath":iconPath}
}
# #删除ID为1的数据,返回的是被改的行数
# async def deleteById(id):
# return await userModel.filter(id=id).delete()
@router.post("/del/{id}", summary="删除"+name, description="根据ID删除(软删除)"+name, responses=func.Responses("删除"+name,1))
async def s_delete_asset(id: int):
res = await MbAsset.filter(pid=id).update(isDel=1)
res = await MbAsset.filter(id=id).update(isDel=1)
return {
"code": 0,
"msg": '',
"result": res
}
以上代码片段定义了三个FastAPI的路由处理函数,分别用于添加、修改(更新)和删除资产(Asset
)信息。这些函数都使用了异步编程模型,并假设与某种数据库模型(MbAsset
)进行交互。下面是对以上三段代码的分析:
1.add_asset
函数
- 功能:添加新的资产信息。
- URL路径:
/
(使用POST方法)。 - 参数:
item
(AssetItem
类型),这是一个Pydantic模型,用于验证和解析请求体中的JSON数据。 - 操作:从
item
中提取必要的信息(如name
、pid
、iconPath
、amount
、sort
),并使用这些信息在数据库中创建一个新的资产记录。这里假设MbAsset.create()
是一个异步方法,用于将新记录添加到数据库中。 - 返回值:一个包含状态码(
code
)、消息(msg
)和结果(result
,即新创建的资产记录)的字典。
2.update_asset
函数
- 功能:更新资产信息(但命名和参数可能有些误导)。
- URL路径:
/edit/{id}/{name}/{iconPath}
(使用POST方法,但通常更新操作使用PUT方法更合适)。 - 参数:
id
(整数,资产的ID)、name
(字符串,新名称)、iconPath
(字符串,新的图标路径)。这些参数直接从URL路径中提取。 - 操作:根据提供的
id
查找资产记录,并更新其name
和iconPath
字段。但这里有个问题:函数名update_asset
暗示了更新操作,但通常我们期望通过请求体来传递要更新的完整数据或至少是一个包含更新字段的字典,而不是直接从URL路径中解析出字段。此外,由于直接从URL路径获取字段,这限制了可以更新的字段数量。 - 返回值:一个包含状态码、消息和结果的字典,但结果仅包含更新的字段(
name
和iconPath
),而不是完整的资产记录。
3.s_delete_asset
函数
- 功能:根据ID软删除资产信息(即标记为已删除,而不是从数据库中物理删除)。
- URL路径:
/del/{id}
(使用POST方法,但删除操作通常使用DELETE方法更合适)。 - 参数:
id
(整数,要删除的资产的ID)。 - 操作:这里有一个问题:函数首先尝试根据
pid
(而不是id
)来更新isDel
字段,这可能是一个错误或误导。然后,它正确地根据id
更新isDel
字段为1,表示该资产已被软删除。这可能是一个复制粘贴错误或逻辑上的混淆。 - 返回值:一个包含状态码、消息和结果的字典,但这里的
result
可能表示受影响的行数或更新操作的结果,具体取决于MbAsset.update()
方法的实现。
4.联系和区别
联系:
- 所有函数都是异步的,使用
async def
定义。 - 它们都与
MbAsset
数据库模型进行交互,执行增删改操作。 - 它们都返回包含状态码、消息和结果的字典。
区别:
- 功能:
add_asset
用于添加新资产,update_asset
(尽管有误导)用于更新资产的特定字段,s_delete_asset
用于软删除资产。 - URL路径和HTTP方法:路径和使用的HTTP方法不同,反映了不同的操作类型(添加、更新、删除)。但请注意,
update_asset
和s_delete_asset
使用的HTTP方法(POST)可能与常规实践(PUT用于更新,DELETE用于删除)不符。 - 参数:
add_asset
通过请求体接收参数(item
),而update_asset
和s_delete_asset
则直接从URL路径中提取参数。 - 操作细节:
update_asset
的实现方式可能不是最佳实践,因为它限制了可以更新的字段数量,并且通常我们期望通过请求体来传递要更新的数据。s_delete_asset
中存在可能的逻辑错误或误导,因为它首先尝试根据pid
更新记录。 - 返回值:虽然格式相似,但
result
字段的具体内容根据操作类型而异。在update_asset
中,它仅包含更新的字段;在其他函数中,它可能包含完整的记录或操作结果。
五、资产分组查询API:有效分组
@router.get("/groups/", summary=name+"分组", description=name+"分组", responses=func.Responses(name+"分组",[RES]))
async def asset_groups():
res = await MbAsset.filter(isDel=0).order_by('pid')
return {
"code": 0,
"result": res
}
这段代码定义了一个FastAPI的路由处理函数,用于获取资产(Asset)的分组信息。在Web开发中,路由处理函数负责处理来自客户端的请求,并根据请求的内容执行相应的操作,然后返回响应给客户端。
通俗理解就是:想象你有一个在线商店,里面卖了很多不同种类的商品。这些商品被分门别类地放在了不同的“分组”里,比如“电子产品”、“家居用品”等。现在,你想要知道这些分组的信息,比如每个分组里有哪些商品,这段代码就是用来获取这些分组信息的工具。
-
设置获取分组的路径:
你告诉服务器:“嘿,服务器,当有人访问/groups/
这个网址时,你就用下面的方法给他们分组的信息。” -
描述这个操作:
你还告诉服务器:“这个操作的名字就是‘分组’(不过这里用了一个变量name
来动态地加在前面,可能是为了更清楚地知道是哪个类型的分组,比如‘电子产品分组’)。而且,我还要在API文档里说明这个操作是干嘛用的,也是‘分组’(同样用了name
变量)。” -
准备返回的信息:
你还告诉服务器:“如果有人请求这个分组信息,我应该怎么告诉他们我返回了什么呢?嗯,我会用一个特定的方式(这里假设是func.Responses
函数来定义)来描述我返回的信息,标题还是‘分组’,但具体怎么描述,要看RES
这个东西。” -
执行操作并返回结果:
当有人真的访问了/groups/
这个网址时,服务器就会开始工作了。它首先去你的“商品数据库”里查找那些没有被“删除标记”(isDel=0
)的分组信息,并且按照“父分组ID”(pid
)来排序这些分组。然后,它会把找到的分组信息打包成一个简单的信息包,里面有个code
表示操作成功(0
通常表示没有错误),还有个result
包含了所有的分组信息。 -
把结果给请求的人:
最后,服务器就把这个信息包发送给请求它的人,说:“这就是你要的分组信息啦!”
六、响应结构Res
RES = {
"id": 1,
"name":"资产类别",
"pid": 0,
"iconPath": "icon-zhifubao",
"amount": 0,
"sort": 0
}
下面是对 RES
字典中各个字段的详细解释:
-
"id"
: 这是一个唯一的标识符,用于区分不同的资产类别或分组。在这个例子中,id
的值是1
,表示这是第一个(或特定编号为1的)资产类别。 -
"name"
: 这个字段存储了资产类别的名称。在这个例子中,名称是"资产类别"
,这可能是一个比较通用的名称,实际使用中可能会更加具体,如"电子产品"
、"家居用品"
等。 -
"pid"
: 这个字段可能代表“父级ID”(Parent ID)。在分层的数据结构中,如树形结构,每个节点(在这个上下文中是资产类别)都可能有一个父节点。pid
为0
通常表示这个节点是根节点,即它没有父节点。如果pid
不是0
,则它指向另一个资产类别的ID,表示这个类别是另一个类别的子类别。 -
"iconPath"
: 这个字段存储了与该资产类别相关联的图标文件的路径或名称。在这个例子中,图标路径是"icon-zhifubao"
,这可能是一个指向某个图标文件的引用,用于在用户界面上表示这个资产类别。 -
"amount"
: 这个字段可能用于表示该资产类别下包含的项目或资产的数量。然而,在这个例子中,amount
的值是0
,这可能意味着这个字段在这个上下文中不被使用,或者它确实表示这个类别下没有资产(尽管这在实际应用中可能不太常见,因为通常这个字段会用于其他目的,如统计)。 -
"sort"
: 这个字段可能用于指定资产类别在列表或界面中的显示顺序。通过修改sort
的值,可以调整类别的排序。在这个例子中,sort
的值是0
,这可能意味着没有特定的排序顺序,或者这个字段需要被动态地设置以反映用户的偏好或业务需求。
七、Pydantic 模型定义:资产项请求体结构
# Pydantic 来使用标准的 Python 类型声明请求体
class AssetItem(BaseModel):
name: Optional[str] = None
pid: int = Field(..., ge=0, description="资产类别ID") # gt=0是>0, ge=0是>=0
# iconPath: Optional[str] = None
iconPath: Optional[str] = None
amount: Optional[float] = None
sort: int
class Config:
schema_extra = {
"example": {
"name": "比特币",
"iconPath": "icon-zhifubao",
"amount": 0,
"pid": 0,
"sort": 0
}
}
这段代码定义了一个使用Pydantic库的BaseModel
类的子类AssetItem
,它主要用于在FastAPI(或其他需要数据验证和解析的Python web框架)中声明和验证HTTP请求体(request body)的数据结构。Pydantic是一个Python库,用于数据解析和设置管理,它通过Python的类型提示(type hints)来定义数据结构,并自动进行数据的验证和转换。
具体来说,AssetItem
类定义了以下字段和它们的特点:
-
name
: 一个可选的字符串(Optional[str]
),表示资产项的名称。由于它是可选的,所以如果没有提供,它将默认为None
。 -
pid
: 一个整数(int
),表示资产类别的ID。这里使用了Field
类来提供额外的验证和描述信息。...
表示这个字段是必需的(即请求体中必须包含这个字段),ge=0
表示这个字段的值必须大于等于0(ge
是greater than or equal to的缩写)。description="资产类别ID"
为这个字段提供了额外的描述信息,这在API文档中非常有用。 -
iconPath
: 一个可选的字符串(Optional[str]
),表示与资产项相关联的图标文件的路径或名称。与name
字段一样,它也是可选的。 -
amount
: 一个可选的浮点数(Optional[float]
),表示资产项的数量或金额。它也是可选的,如果没有提供,将默认为None
。 -
sort
: 一个整数(int
),表示资产项在列表或界面中的排序位置。由于它没有使用Optional
或Field
来指定为可选或提供额外的验证条件,因此它被认为是必需的,且没有指定额外的最小值或最大值限制。
Config
类内部的schema_extra
字典为整个模型提供了一个额外的example
字段,这是一个JSON对象,展示了如何使用这个模型的一个示例。这个示例将被用于API文档,帮助开发者了解如何构造请求体。