查询功能:分析查询功能后端代码

@router.get("/list", summary="查看用户列表")
async def list_user(
    page: int = Query(1, description="页码"),
    page_size: int = Query(10, description="每页数量"),
    username: str = Query("", description="用户名称,用于搜索"),
    email: str = Query("", description="邮箱地址"),
    dept_id: int = Query(None, description="部门ID"),
):
    q = Q()
    if username:
        q &= Q(username__contains=username)
    if email:
        q &= Q(email__contains=email)
    if dept_id is not None:
        q &= Q(dept_id=dept_id)
    total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q)
    data = [await obj.to_dict(m2m=True, exclude_fields=["password"]) for obj in user_objs]
    for item in data:
        dept_id = item.pop("dept_id", None)
        item["dept"] = await (await dept_controller.get(id=dept_id)).to_dict() if dept_id else {}

    return SuccessExtra(data=data, total=total, page=page, page_size=page_size)

1. 路由定义

@router.get("/list", summary="查看用户列表")

  • 这行代码定义了一个 GET 请求的路由,路径为 /list,用于获取用户列表信息。summary 参数用于在 API 文档中简要描述该接口的功能,这里描述为 “查看用户列表”。

2. 函数定义

async def list_user(

  • 定义了一个异步函数 list_user,因为 FastAPI 支持异步操作,使用 asyncawait 可以提高程序的性能和响应速度,特别是在涉及 I/O 操作如数据库查询时。

3. 参数定义

page: int = Query(1, description="页码"),

  • 定义了一个整型参数 page,默认值为 1。Query 是 FastAPI 中用于定义查询参数的函数,description 参数用于在 API 文档中对该参数进行详细描述,这里表示该参数是用于分页的页码。

page_size: int = Query(10, description="每页数量"),

  • 定义了一个整型参数 page_size,默认值为 10,用于指定每页显示的数量,同样使用 Query 进行定义并添加描述。

username: str = Query("", description="用户名称,用于搜索"),

  • 定义了一个字符串参数 username,默认值为空字符串,用于根据用户名称进行搜索。

email: str = Query("", description="邮箱地址"),

  • 定义了一个字符串参数 email,默认值为空字符串,用于根据邮箱地址进行搜索。

dept_id: int = Query(None, description="部门ID"),):

  • 定义了一个整型参数 dept_id,默认值为 None,用于根据部门 ID 进行筛选。

4. 查询条件构造

q = Q()

  • 初始化一个查询条件对象 qQ 通常是一个用于构建复杂查询条件的类,在 ORM(如 Tortoise-ORM)中常用到,用于组合多个查询条件。

if username:
    q &= Q(username__contains=username)
if email:
    q &= Q(email__contains=email)
if dept_id is not None:
    q &= Q(dept_id=dept_id)
  • 这几行代码根据传入的参数构建查询条件。&= 运算符用于将多个条件进行组合,表示 “与” 的关系。

  • 如果 username 不为空,则添加一个条件,要求用户名包含传入的 username 值。

  • 如果 email 不为空,则添加一个条件,要求邮箱地址包含传入的 email 值。

  • 如果 dept_id 不为 None,则添加一个条件,要求部门 ID 等于传入的 dept_id 值。

5. 调用控制器获取数据

total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q)

  • 调用 user_controller 中的 list 方法,传入分页参数 pagepage_size 和构建好的查询条件 q,获取用户列表数据和总记录数 totalawait 关键字用于等待异步操作完成,因为 list 方法可能涉及到异步的数据库查询操作。

6. 数据处理

data = [await obj.to_dict(m2m=True, exclude_fields=["password"]) for obj in user_objs]

  • 使用列表推导式对获取到的用户对象 user_objs 进行处理,将每个对象转换为字典格式。await obj.to_dict(m2m=True, exclude_fields=["password"]) 表示调用对象的 to_dict 方法,m2m=True 可能表示包含多对多关系的数据,exclude_fields=["password"] 则表示在转换时排除密码字段,以保护用户密码信息不被泄露。

7. 补充部门信息

for item in data:
    dept_id = item.pop("dept_id", None)
    item["dept"] = await (await dept_controller.get(id=dept_id)).to_dict() if dept_id else {}
  • 遍历处理后的用户数据列表 data

  • item.pop("dept_id", None) 用于获取并移除用户数据中的 dept_id 字段,避免重复数据并提高数据传输效率。

  • 如果 dept_id 存在,则通过 dept_controller.get(id=dept_id) 获取对应的部门对象,并调用其 to_dict 方法转换为字典格式,作为用户的部门信息存储到 item["dept"] 中;如果 dept_id 不存在,则将 item["dept"] 设置为一个空字典。

8. 返回响应

return SuccessExtra(data=data, total=total, page=page, page_size=page_size)

  • 返回一个 SuccessExtra 类型的响应对象,包含用户数据列表 data、总记录数 total、当前页码 page 和每页数量 page_size,用于向前端提供分页后的用户列表数据以及分页相关信息,方便前端进行分页展示等操作。

在实际开发中,还有一些实用技巧可以帮助新手更好地理解和运用代码:

前端调用示例

在 Vue3 中,可以使用 axios 来调用该接口获取用户列表数据:

import axios from 'axios';

const getUserList = async (page = 1, page_size = 10, username = '', email = '', dept_id = null) => {
  try {
    const response = await axios.get('/api/list', {
      params: { page, page_size, username, email, dept_id }
    });
    return response.data;
  } catch (error) {
    console.error('获取用户列表失败:', error);
    return null;
  }
};

在组件中调用该函数获取数据后,可以使用 NaiveUI 的表格组件 n-data-table 来展示用户列表数据,并结合分页组件 n-pagination 实现分页功能:

<template>
  <div>
    <n-data-table :columns="columns" :data="userList" />
    <n-pagination v-model:page="currentPage" :page-count="totalPages" />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { getUserList } from './api/userApi';

const userList = ref([]);
const total = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);

const columns = [
  { title: '用户名', key: 'username' },
  { title: '邮箱', key: 'email' },
  { title: '部门', key: 'dept.name' }, // 假设部门信息中有 name 字段
];

onMounted(async () => {
  await fetchUserList();
});

const fetchUserList = async () => {
  const data = await getUserList(currentPage.value, pageSize.value);
  if (data) {
    userList.value = data.data;
    total.value = data.total;
  }
};

// 分页切换时重新获取数据
watch(currentPage, fetchUserList);
</script>

数据验证

在 FastAPI 中,可以使用 Pydantic 模型对请求参数进行验证,确保接收到的参数符合预期的格式和类型。例如,可以定义一个请求模型:

from pydantic import BaseModel, Field

class UserListQuery(BaseModel):
    page: int = Field(1, ge=1, description="页码,必须大于等于1")
    page_size: int = Field(10, ge=1, le=100, description="每页数量,必须大于等于1且小于等于100")
    username: str = Field("", max_length=50, description="用户名称,最大长度50")
    email: str = Field("", description="邮箱地址")
    dept_id: int = Field(None, description="部门ID")

然后在路由函数中使用该模型作为参数:

@router.get("/list", summary="查看用户列表")
async def list_user(query: UserListQuery = Depends()):
    q = Q()
    if query.username:
        q &= Q(username__contains=query.username)
    if query.email:
        q &= Q(email__contains=query.email)
    if query.dept_id is not None:
        q &= Q(dept_id=query.dept_id)
    total, user_objs = await user_controller.list(page=query.page, page_size=query.page_size, search=q)
    # 后续代码不变

这样可以更好地对请求参数进行验证和管理,提高代码的健壮性和可维护性。

错误处理

在实际开发中,可能会出现各种错误情况,如数据库查询失败、网络请求超时等。为了更好地处理这些错误,可以使用 FastAPI 的异常处理机制。例如,定义一个自定义异常类:

from fastapi import HTTPException

class UserNotFoundError(HTTPException):
    def __init__(self):
        super().__init__(status_code=404, detail="用户未找到")

在代码中,如果出现用户未找到的情况,可以抛出该异常:

user = await User.get(id=user_id)
if not user:
    raise UserNotFoundError()

在前端,可以对返回的错误信息进行统一处理,给用户友好的提示:

axios.get('/api/list', { params })
  .then(response => {
    // 处理成功情况
  })
  .catch(error => {
    if (error.response) {
      // 服务器返回了错误响应
      console.error('错误状态码:', error.response.status);
      console.error('错误信息:', error.response.data.detail);
      // 根据不同的错误状态码和信息,给用户相应的提示
    } else if (error.request) {
      // 请求发送失败(如网络问题)
      console.error('请求发送失败:', error.message);
    } else {
      // 请求配置错误等其他情况
      console.error('请求配置错误:', error.message);
    }
  });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值