Python异步开发实践:FastAPI与Tortoise-ORM的高效协同工作指南

一、前言

在 Python Web 开发领域,框架的选择始终与场景深度绑定:Flask 的 “微内核 + 插件” 模式赋予开发者自由组装功能的灵活性,却在大型项目中暴露组件碎片化与异步支持不足的问题;Django 的 “全栈一站式” 架构降低开发门槛,但其同步执行机制在高并发 API 服务中逐渐力不从心;而 FastAPI 以异步原生、类型安全和高性能设计,重新定义了微服务与 I/O 密集型场景的开发体验。

数据库交互层同样存在瓶颈:SQLAlchemy 的异步模式依赖 asyncio 适配,复杂查询优化与连接池管理难以突破传统框架限制;Peewee 等轻量 ORM 专注同步场景,无法满足高并发下的异步协作需求;Django ORM 虽数据建模稳健,却因深度耦合框架、异步支持滞后,难以成为独立场景的最优解。

FastAPI 与 Tortoise-ORM 的组合正是为破解这些痛点而生:前者以高性能异步引擎和 OpenAPI 规范,适配云原生微服务架构;后者以 “轻量异步优先” 设计,支持多数据库方言并提供高效连接池管理。二者协同打通 “框架异步能力” 与 “ORM 执行效率” 的断层 ——FastAPI 的依赖注入与 Tortoise-ORM 的模型定义无缝衔接,异步路由与数据库操作形成流水线协作,让开发者在享受类型提示高效开发的同时,确保高并发下的服务稳定性。本文将聚焦这对 “异步拍档” 的实战实践,解析如何突破传统局限,实现开发效率与性能的双重提升。

二、FastAPI 与 Tortoise-ORM 的核心优势

在异步编程重塑后端开发范式的时代,FastAPI 与 Tortoise-ORM 分别以「API 开发效率」和「数据持久化优雅性」为支点,构建了一套完整的异步技术栈。两者的设计哲学既保持独立技术深度,又在工程实践中形成互补,成为现代Python后端开发的黄金组合。对于习惯于前后端分离的小伙伴来说应该会更适应。

2.1 FastAPI:异步优先的 API 开发利器

FastAPI 的诞生源于对 Python Web 开发效率与性能瓶颈的突破:随着异步编程在高并发、I/O 密集型场景的需求激增,传统框架如 Flask 的异步支持薄弱、Django 的同步机制低效等问题逐渐凸显,开发者亟需一种既能发挥 Python 类型提示优势、又能原生支持异步的现代框架。

2018 年,开发者 Sebastián Ramírez 结合自身经验,基于 Starlette 和 Pydantic 构建了 FastAPI,通过类型安全的设计、自动生成 API 文档、高效的异步引擎,解决了传统框架在开发效率与性能上的痛点。其高性能、易上手、强类型检查等特性迅速获得社区青睐,成为微服务、实时 API 等场景的首选框架,推动 Python Web 开发进入 “异步 + 类型安全” 的高效时代。

FastAPI 的设计哲学围绕 "高效开发" 与 "生产级健壮性" 展开,其技术优势可从四个维度解析:

异步原生支持:重构并发处理模型 

FastAPI 基于 Starlette 异步框架 构建,完全遵循 Python 异步编程规范(async/await),从底层架构支持非阻塞 I/O 操作。通过 uvloop(高性能事件循环)或原生 asyncio,将 I/O 操作与 CPU 计算分离,避免传统同步框架中线程上下文切换的开销。

这种设计使其在处理数据库查询、文件读写、第三方接口调用等 I/O 密集型任务时,能充分利用事件循环机制,实现单进程支撑 10k+ 并发连接 的高性能表现。 

因此一定程度上也算是python在web开发或者高并发环境开发的一次进步吧,毕竟长期以来python给我的印象就是工具语言或者机器学习类语言,并发开发场景下远不如Java,更不谈Go。

类型提示革命:构建编译级安全防线 

借助 Pydantic 数据模型,FastAPI 将 Python 的类型提示从「开发辅助」提升为「运行时校验」的核心能力:

  • 参数校验自动化:对请求体、路径参数、查询参数进行强类型校验,自动拒绝非法输入(如将字符串转为数字失败时返回 422 错误)

    python

    from pydantic import BaseModel  
    class UserCreate(BaseModel):  
        username: str  # 自动校验字符串长度(默认 0 < len <= 1e5)  
        age: int       # 自动拒绝非整数输入  
    
  • IDE 深度集成:VS Code/PyCharm 可实时捕获类型错误,结合 mypy 静态检查,减少 70% 的运行时类型错误
  • 文档即代码:基于 OpenAPI 3.0 标准,自动生成交互式接口文档(Swagger UI/ReDoc),字段描述、示例数据、响应格式与代码完全同步,前端对接效率提升 50%
  • 轻量架构:聚焦 API 开发的「精准打击」 

    与 Django 等全功能框架相比,FastAPI 选择「做精不做全」的策略:

  • 功能聚焦:仅包含 API 开发必需组件(路由、校验、文档),剔除模板引擎、表单系统等 Web 开发非核心功能
  • 性能优势:启动时间约为 Django 的 1/5(实测 100ms vs 500ms+),内存占用减少 40%,更适合微服务架构中的快速部署与弹性扩展
  • 生态兼容:无缝集成 Celery(异步任务)、Redis(缓存)、OAuth2(认证)等工具,形成完整的 API 服务栈
  •  2.2Tortoise-ORM:异步ORM新范式

    Tortoise – ORM 的诞生源于 Python 异步 Web 开发浪潮下对高效数据库操作方案的渴求。传统 ORM 如 SQLAlchemy、Django ORM 多为同步设计,在高并发、I/O 密集场景下成为性能瓶颈。开发者构建高性能异步 Web 应用时,急需一款能与异步框架无缝集成、支持多数据库且 API 简洁的 ORM。

    2018 年左右,社区开发者共同推出 Tortoise – ORM,专注异步操作。此后,它不断发展,增加数据库支持、优化 API 设计、提升性能与稳定性。其优势显著,异步特性适配异步框架,链式查询语法简洁直观,支持多数据库提供灵活选择,高效连接池管理保障资源利用。Tortoise – ORM 满足了开发者需求,成为 Python 异步开发中强大便捷的数据库操作解决方案。

    作为首款原生支持异步操作的 Python ORM,Tortoise-ORM 重新定义了数据持久化的开发体验,核心优势体现在对传统 ORM 痛点的针对性解决,主要有如下几点:

    非阻塞数据库操作 

    传统 ORM(如 SQLAlchemy 同步模式、Django ORM)在执行数据库操作时会阻塞事件循环,导致异步框架性能优势无法发挥。而Tortoise-ORM 底层集成 asyncpg(PostgreSQL)与 aiomysql(MySQL)异步驱动,实现真正的 IO 与 CPU 并行处理

  • 代码范式:通过 await 关键字执行查询,避免线程池中转(对比 SQLAlchemy 需使用 asyncio.run_in_executor 包装同步操作)
    # 异步创建用户(非阻塞)  
    user = await User.create(username="test", email="test@example.com")  
    
  • 连接池优化:默认支持连接池配置(connection_pool_size/max_connections),减少频繁创建连接的开销,提升数据库交互效率
  • 目前Tortoise ORM 支持一下数据库的异步驱动使用:

  • PostgreSQL >= 9.4(使用 asyncpg

  • SQLite (使用aiosqlite

  • MySQL/MariaDB (使用asyncmy)

  • Microsoft SQL Server/Oracle (使用 asyncodbc

  •  极简语法设计

    继承 Pydantic 的「声明式模型」设计哲学,Tortoise-ORM 的数据模型定义简洁直观,学习成本比 SQLAlchemy 异步模式降低 40%。在定义字段时Tortoise-ORM支持 CharField/IntField/DatetimeField 等常用类型,内置 auto_now_add/unique 等修饰符使用。

    from tortoise.models import Model  
    from tortoise import fields  
    
    class User(Model):  
        id = fields.IntField(pk=True)  
        username = fields.CharField(max_length=50, unique=True)  
        create_time = fields.DatetimeField(auto_now_add=True)  # 自动填充创建时间  

    除此以外,Tortoise-ORM也支持 filter()/order_by()/limit() 等链式操作,复杂查询可通过 Q 对象组合(类似 Django ORM) :

    # 查询最近 10 个活跃用户  
    users = await User.filter(is_active=True).order_by("-create_time").limit(10)  

     轻量级架构

    与 SQLAlchemy(150k+ 行代码)相比,Tortoise-ORM 核心代码仅 10k 行级别,内存占用减少 30%,具有以下优势:

  • 启动速度:初始化时间比 SQLAlchemy 异步模式快 20%,适合 serverless 等对启动时间敏感的场景。
  • 依赖简单:仅依赖 pydantic 和数据库驱动,无额外复杂组件,降低项目维护成本。
  • Tortoise-ORM与传统 ORM 对比 

    特性 Tortoise-ORM SQLAlchemy (异步) Django ORM
    异步原生支持 ✅(内置 asyncpg/aiomysql) ❌(需手动配置线程池) ❌(仅同步模式)
    类型提示集成 深度集成(模型定义即类型约束) 部分支持(需手动标注 AsyncSession 有限支持(仅字段类型校验)
    文档自动生成 与 FastAPI 无缝结合(模型即文档) 需手动编写 OpenAPI 描述 无(需额外插件)
    学习曲线 低(Pydantic 风格语法) 中(需掌握同步 / 异步双模式) 高(复杂的 ORM 生态)
    内存占用(单实例) 约 80MB 约 110MB 约 150MB

     三、FastAPI 与 Tortoise-ORM在经典开发场景下的应用流程

    这里我们结合最常见的教师、学生、课程、成绩等基础增删改查业务来实现整个项目,数据库采用pgsql,数据表如下:

    /*
     Navicat Premium Dump SQL
    
     Source Server         : 本地PGSQL
     Source Server Type    : PostgreSQL
     Source Server Version : 170000 (170000)
     Source Host           : localhost:5432
     Source Catalog        : python_test
     Source Schema         : public
    
     Target Server Type    : PostgreSQL
     Target Server Version : 170000 (170000)
     File Encoding         : 65001
    
     Date: 10/04/2025 09:38:40
    */
    
    
    -- ----------------------------
    -- Table structure for teacher
    -- ----------------------------
    DROP TABLE IF EXISTS "public"."teacher";
    CREATE TABLE "public"."teacher" (
      "tid" int4,
      "tname" text COLLATE "pg_catalog"."default"
    )
    ;
    
    -- ----------------------------
    -- Records of teacher
    -- ----------------------------
    INSERT INTO "public"."teacher" VALUES (1, '张老师');
    INSERT INTO "public"."teacher" VALUES (2, '李老师');
    INSERT INTO "public"."teacher" VALUES (3, '张雪峰');
    INSERT INTO "public"."teacher" VALUES (4, '刘广笔');
    INSERT INTO "public"."teacher" VALUES (5, '赵六');
    INSERT INTO "public"."teacher" VALUES (6, '李志月');
    
    -- ----------------------------
    -- Table structure for student
    -- ----------------------------
    DROP TABLE IF EXISTS "public"."student";
    CREATE TABLE "public"."student" (
      "sid" int4,
      "sname" text COLLATE "pg_catalog"."default",
      "sage" int4,
      "ssex" text COLLATE "pg_catalog"."default"
    )
    ;
    
    -- ----------------------------
    -- Records of student
    -- ----------------------------
    INSERT INTO "public"."student" VALUES (1, '张三', 22, '男');
    INSERT INTO "public"."student" VALUES (2, '李四', 19, '男');
    INSERT INTO "public"."student" VALUES (3, '王五', 20, '女');
    INSERT INTO "public"."student" VALUES (4, '黎治跃', 21, '男');
    INSERT INTO "public"."student" VALUES (5, '李帅', 21, '男');
    INSERT INTO "public"."student" VALUES (6, '黄江小', 20, '女');
    INSERT INTO "public"."student" VALUES (7, '菜菜懒', 19, '女');
    INSERT INTO "public"."student" VALUES (8, '小花', 20, '女');
    INSERT INTO "public"."student" VALUES (9, '闷油瓶', 25, '男');
    INSERT INTO "public"."student" VALUES (10, '曾小贤', 28, '男');
    
    -- ----------------------------
    -- Table structure for sc
    -- ----------------------------
    DROP TABLE IF EXISTS "public"."sc";
    CREATE TABLE "public"."sc" (
      "sid" int4,
      "cid" int4,
      "score" int4
    )
    ;
    
    -- ----------------------------
    -- Records of sc
    -- ----------------------------
    INSERT INTO "public"."sc" VALUES (1, 2, 85);
    INSERT INTO "public"."sc" VALUES (1, 5, 81);
    INSERT INTO "public"."sc" VALUES (1, 6, 63);
    INSERT INTO "public"."sc" VALUES (2, 5, 95);
    INSERT INTO "public"."sc" VALUES (2, 6, 85);
    INSERT INTO "public"."sc" VALUES (3, 1, 56);
    INSERT INTO "public"."sc" VALUES (3, 5, 63);
    INSERT INTO "public"."sc" VALUES (3, 6, 76);
    INSERT INTO "public"."sc" VALUES (4, 1, 65);
    INSERT INTO "public"."sc" VALUES (4, 5, 87);
    INSERT INTO "public"."sc" VALUES (4, 8, 74);
    INSERT INTO "public"."sc" VALUES (5, 1, 85);
    INSERT INTO "public"."sc" VALUES (5, 5, 90);
    INSERT INTO "public"."sc" VALUES (5, 9, 91);
    INSERT INTO "public"."sc" VALUES (6, 1, 86);
    INSERT INTO "public"."sc" VALUES (6, 5, 68);
    INSERT INTO "public"."sc" VALUES (7, 1, 75);
    INSERT INTO "public"."sc" VALUES (7, 5, 63);
    INSERT INTO "public"."sc" VALUES (8, 1, 68);
    INSERT INTO "public"."sc" VALUES (8, 5, 76);
    INSERT INTO "public"."sc" VALUES (8, 8, 88);
    INSERT INTO "public"."sc" VALUES (9, 1, 70);
    INSERT INTO "public"."sc" VALUES (9, 5, 92);
    INSERT INTO "public"."sc" VALUES (10, 1, 90);
    INSERT INTO "public"."sc" VALUES (10, 5, 86);
    INSERT INTO "public"."sc" VALUES (1, 1, 50);
    INSERT INTO "public"."sc" VALUES (2, 1, 75);
    

    通过python虚拟环境构建项目,这里构建步骤省略不计,但是需要注意提前装备好两个开发框架的依赖:

    pip install fastapi uvicorn tortoise-orm pydantic psycopg2

    准备好后即可开始项目搭建工作。 

    Step1:模型定义

    在 app/models/models.py 中定义Tortoise ORM模型,用于将 Python 类与数据库表进行映射。相当于java开发中的实体类,每个类对应一个数据库表,类的属性对应表的列。通过定义这些模型,我们可以使用 Python 代码来操作数据库,而不需要直接编写 SQL 语句。 

    from tortoise.models import Model
    from tortoise import fields
    
    
    class Teacher(Model):
        """
        教师模型
        - tid: 教师表ID (主键)
        - tname: 教师姓名
        """
        tid = fields.IntField(pk=True)
        tname = fields.CharField(max_length=255, description="教师姓名")
    
        class Meta:
            table = "teacher"
    
    
    class Student(Model):
        """
        学生模型
        - sid: 学生表ID (主键)
        - sname: 学生姓名
        - sage: 学生年龄
        - ssex: 学生性别
        """
        sid = fields.IntField(pk=True)
        sname = fields.CharField(max_length=255, description="学生姓名")
        sage = fields.IntField(description="学生年龄")
        ssex = fields.CharField(max_length=10, description="学生性别")
    
        class Meta:
            table = "student"
    
    
    class SC(Model):
        """
        成绩模型
        - id: 成绩表ID (主键)
        - sid: 学生ID
        - cid: 课程ID
        - score: 分数
        """
        id = fields.IntField(pk=True)
        sid = fields.IntField(description="学生ID")
        cid = fields.IntField(description="课程ID")
        score = fields.IntField(description="分数")
    
        class Meta:
            table = "sc"
    
    
    class Course(Model):
        """
        课程模型
        - cid: 课程表ID (主键)
        - cname: 课程名称
        - tid: 教师ID (外键)
        """
        cid = fields.IntField(pk=True)
        cname = fields.CharField(max_length=255, description="课程名称")
        tid = fields.IntField(description="教师ID")
    
        class Meta:
            table = "course"
    

     这里我们定义的就是对应的数据库中的四个数据表对象及字段信息。

    Step2:定义Pydantic模型

    在 app/schemas/schemas.py 中定义pydantic 模型,pydantic是一个用于数据解析和验证的 Python 库,特别适用于构建 API 和 Web 应用时处理请求和响应数据。

    # schemas.py
    from pydantic import BaseModel
    
    
    class TeacherBase(BaseModel):
        tname: str
    
    
    class TeacherCreate(TeacherBase):
        pass
    
    
    class Teacher(TeacherBase):
        tid: int
    
        class Config:
            from_attributes = True  # 注意这里已经调整了
    
    
    class StudentBase(BaseModel):
        sname: str
        sage: int
        ssex: str
    
    
    class StudentCreate(StudentBase):
        pass
    
    
    class Student(StudentBase):
        sid: int
    
        class Config:
            from_attributes = True
    
    
    class SCBase(BaseModel):
        sid: int
        cid: int
        score: int
    
    
    class SCCreate(SCBase):
        pass
    
    
    class SC(SCBase):
        class Config:
            from_attributes = True
    
    
    class CourseBase(BaseModel):
        cname: str
        tid: int
    
    
    class CourseCreate(CourseBase):
        pass
    
    
    class Course(CourseBase):
        cid: int
    
        class Config:
            from_attributes = True
    

    这里定义了用于管理教师、学生和课程的信息以及他们之间的关系的数据模型。通过继承基本模型(如 TeacherBaseStudentBaseCourseBase),可以创建不同的子模型来适应不同的操作场景(如创建新记录)。设置 from_attributes = True 使得这些模型类可以直接从对象的属性初始化,便于与数据库交互,从而简化了数据处理流程。

    Step3:项目配置

    这里主要有四个方面,首先就是主要的数据库配置,由于我们使用的pgsql,而在异步项目中我们可以使用psql的异步驱动来配置:

    # config.py
    from pydantic_settings import BaseSettings
    
    
    class Settings(BaseSettings):
        # 数据库连接URL,从.env文件中读取
        DB_USER: str = "postgres"
        DB_PASSWORD: str = "123456"
        DB_HOST: str = "localhost"
        DB_PORT: int = 5432
        DB_NAME: str = "python_test"
        # 日志级别,默认值为INFO
        log_level: str = "INFO"
    
        class Config:
            # 指定.env文件路径
            env_file = ".env"
            # 允许使用环境变量前缀
            env_file_encoding = 'utf-8'
    
    
    # 创建配置实例
    settings = Settings()
    

     这里我将数据库配置分开进行处理,config.py记录数据库配置信息,用的是明文信息,实际开发中我们可以通过环境配置文件读取,然后配置数据库初始化信息database.py:

    from tortoise import Tortoise
    from tortoise.contrib.fastapi import register_tortoise
    from app.core.config import settings
    from fastapi import FastAPI
    import logging
    
    TORTOISE_ORM = {
        "connections": {
            "default": f"asyncpg://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}"
        },
        "apps": {
            "models": {
                "models": ["app.models.models"],
                "default_connection": "default",
            },
        },
        "use_tz": True,
        "timezone": "Asia/Shanghai"
    }
    
    
    async def init_database():
        """
        初始化数据库连接
        """
        # 设置 Tortoise ORM 的日志级别为 DEBUG
        logger = logging.getLogger("tortoise")
        logger.setLevel(logging.DEBUG)
        
        await Tortoise.init(
            db_url=TORTOISE_ORM["connections"]["default"],
            modules={"models": TORTOISE_ORM["apps"]["models"]["models"]},
            timezone=TORTOISE_ORM["timezone"],
            use_tz=TORTOISE_ORM["use_tz"]
        )
        # 自动根据models定义的model生成数据库表结构
        # await Tortoise.generate_schemas()
    
    
    async def close_database():
        """
        关闭数据库连接
        """
        await Tortoise.close_connections()
    
    
    def register_database(app: FastAPI):
        """
        注册数据库到FastAPI应用
        
        Args:
            app: FastAPI应用实例
        """
        register_tortoise(
            app,
            config=TORTOISE_ORM,
            generate_schemas=False,
            add_exception_handlers=True,
        )
    

    需要使用时注册到项目中即可进行使用。除此以外,日志记录在日常开发中也是不可或缺的一部分,这里我们为了检查业务执行情况,将运行日志、sql执行日志与Tortoise ORM日志分为3个单独文件保存到本地:

    import logging
    import os
    from logging.handlers import RotatingFileHandler, WatchedFileHandler
    from pathlib import Path
    from app.core.config import settings
    
    # 创建日志目录
    log_dir = Path("logs")
    log_dir.mkdir(exist_ok=True)
    
    # 定义日志格式
    log_format = logging.Formatter(
        "%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s"
    )
    
    # SQL日志格式
    sql_log_format = logging.Formatter(
        "%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s"
    )
    
    
    def setup_logger(name, log_file, level=logging.DEBUG, formatter=None):
        """
        设置并返回一个自定义的日志记录器
        
        Args:
            name: 日志记录器名称
            log_file: 日志文件路径
            level: 日志级别,默认为DEBUG
            formatter: 自定义的日志格式,如果为None则使用默认格式
        
        Returns:
            logging.Logger: 配置好的日志记录器
        """
        logger = logging.getLogger(name)
        logger.setLevel(level)
    
        # 如果已经有处理器,不重复添加
        if logger.handlers:
            return logger
    
        # 创建文件处理器
        file_handler = RotatingFileHandler(
            log_file,
            maxBytes=10 * 1024 * 1024,  # 10MB
            backupCount=7,
            encoding='utf-8'
        )
        file_handler.setFormatter(formatter or log_format)
    
        # 创建控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(formatter or log_format)
    
        # 添加处理器到日志记录器
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
    
        return logger
    
    
    # 创建应用程序日志记录器
    app_logger = setup_logger(
        "app",
        log_dir / "app.log",
        level=getattr(logging, settings.log_level.upper())
    )
    
    # 创建Tortoise ORM日志记录器
    tortoise_logger = setup_logger(
        "tortoise",
        log_dir / "tortoise.log",
        level=getattr(logging, settings.log_level.upper())
    )
    
    # 创建数据库SQL日志记录器
    db_logger = logging.getLogger("db_client")
    db_logger.setLevel(logging.DEBUG)  # 设置为DEBUG级别以记录所有SQL查询
    
    # 创建SQL日志文件处理器
    sql_handler = WatchedFileHandler(
        filename=log_dir / "db_client.log",
        encoding='utf-8'
    )
    sql_handler.setFormatter(sql_log_format)
    db_logger.addHandler(sql_handler)
    
    # 确保SQL日志记录器不会向上传播到根记录器
    db_logger.propagate = False
    

    为了统一规范接口响应数据和异常,我们可以像Java开发中在这里进行定义,首先设置统一返回值类result.py:

    from typing import Generic, TypeVar, Optional
    from pydantic import BaseModel
    
    T = TypeVar('T')
    
    
    class Result(BaseModel, Generic[T]):
        """
        统一响应模型基类
        """
        code: int
        message: str
        data: Optional[T] = None
    
    
    class SuccessResult(Result[T]):
        """
        成功响应模型
        """
    
        def __init__(self, data: T = None, message: str = "操作成功"):
            super().__init__(code=200, message=message, data=data)
    
    
    class ErrorResult(Result[T]):
        """
        错误响应模型
        """
    
        def __init__(self, code: int = 400, message: str = "操作失败", data: T = None):
            super().__init__(code=code, message=message, data=data)
    
    
    def success(data: T = None, message: str = "操作成功") -> SuccessResult[T]:
        """
        快速创建成功响应
        """
        return SuccessResult(data=data, message=message)
    
    
    def error(code: int = 400, message: str = "操作失败", data: T = None) -> ErrorResult[T]:
        """
        快速创建错误响应
        """
        return ErrorResult(code=code, message=message, data=data)
    

    这里我们主要使用了Python的类型提示(type hints)和Pydantic库来定义一个通用的响应模型,并在此基础上区分成功和错误的响应,让我们接口响应数据格式更规范。然后配置一些可能在项目运行中出现的全局异常处理exception.py:

    from fastapi import HTTPException, Request
    from fastapi.responses import JSONResponse
    from app.core.result import error
    import logging
    from typing import Any
    
    logger = logging.getLogger("app")
    
    
    class AppException(Exception):
        """基础应用异常"""
    
        def __init__(self, code: int = 400, message: str = "请求错误", detail: Any = None):
            self.code = code
            self.message = message
            self.detail = detail
    
    
    class NotFoundException(AppException):
        """资源未找到异常"""
    
        def __init__(self, message: str = "资源未找到", detail: Any = None):
            super().__init__(404, message, detail)
    
    
    
    class ValidationException(AppException):
        """参数验证异常"""
    
        def __init__(self, message: str = "参数验证失败", detail: Any = None):
            super().__init__(422, message, detail)
    
    
    async def global_exception_handler(request: Request, exc: Exception):
        """全局异常处理器"""
        logger.error(f"请求异常: {str(exc)}", exc_info=True)
    
        if isinstance(exc, AppException):
            return JSONResponse(
                status_code=exc.code,
                content=error(code=exc.code, message=exc.message, data=exc.detail).dict()
            )
        elif isinstance(exc, HTTPException):
            return JSONResponse(
                status_code=exc.status_code,
                content=error(code=exc.status_code, message=str(exc.detail)).dict()
            )
    
        # 处理未捕获的异常
        return JSONResponse(
            status_code=500,
            content=error(code=500, message="服务器内部错误").dict()
        )
    
    
    def register_exception_handlers(app):
        """注册全局异常处理器"""
        app.add_exception_handler(Exception, global_exception_handler)
    

    Step4:定义具体的业务逻辑

    这里就相当于MVC开发中的service层,但需要使用异步函数。用于处理与教师、学生、课程和成绩相关的CRUD(创建、读取、更新、删除)操作。每个函数都接受特定类型的参数并返回一个字典,该字典包含操作的结果信息。

    from typing import Dict, Any
    
    from app.core.result import success, error
    from app.models.models import Teacher, Student, SC, Course
    from app.schemas.schemas import TeacherCreate, StudentCreate, SCCreate, CourseCreate
    from fastapi.encoders import jsonable_encoder  #  解决序列化问题
    
    
    async def create_teacher(teacher: TeacherCreate) -> Dict[str, Any]:
        """
        创建教师业务逻辑
        :param teacher: 教师创建信息
        :return: 创建的教师对象
        """
        try:
            teacher_obj = await Teacher.create(**teacher.dict())
            return success(jsonable_encoder(teacher_obj))
        except Exception as e:
            return error(400, f"创建教师失败: {str(e)}")
    
    
    async def get_teacher(teacher_id: int) -> Dict[str, Any]:
        """
        获取教师信息业务逻辑
        :param teacher_id: 教师ID
        :return: 教师对象
        """
        try:
            teacher = await Teacher.get(tid=teacher_id)
            if not teacher:
                return error(404, "教师未找到", {"teacher_id": teacher_id})
            return success(jsonable_encoder(teacher))
        except Exception as e:
            return error(400, f"获取教师信息失败: {str(e)}")
    
    
    async def update_teacher(teacher_id: int, teacher: TeacherCreate) -> Dict[str, Any]:
        """
        更新教师信息业务逻辑
        :param teacher_id: 教师ID
        :param teacher: 教师更新信息
        :return: 更新后的教师对象
        """
        try:
            db_teacher = await Teacher.get(tid=teacher_id)
            if not db_teacher:
                return error(404, "教师未找到", {"teacher_id": teacher_id})
            updated_teacher = await db_teacher.update_from_dict(teacher.dict()).save()
            return success(jsonable_encoder(updated_teacher))
        except Exception as e:
            return error(400, f"更新教师信息失败: {str(e)}")
    
    
    async def delete_teacher(teacher_id: int) -> Dict[str, str]:
        """
        删除教师业务逻辑
        :param teacher_id: 教师ID
        :return: 删除结果
        """
        try:
            db_teacher = await Teacher.get(tid=teacher_id)
            if not db_teacher:
                return error(404, "教师未找到", {"teacher_id": teacher_id})
            await db_teacher.delete()
            return success(message="教师删除成功")
        except Exception as e:
            return error(400, f"删除教师失败: {str(e)}")
    
    
    async def create_student(student: StudentCreate) -> Dict[str, Any]:
        """
        创建学生业务逻辑
        :param student: 学生创建信息
        :return: 创建的学生对象
        """
        try:
            student_obj = await Student.create(**student.dict())
            return success(jsonable_encoder(student_obj))
        except Exception as e:
            return error(400, f"创建学生失败: {str(e)}")
    
    
    async def get_student(student_id: int) -> Dict[str, Any]:
        """
        获取学生信息业务逻辑
        :param student_id: 学生ID
        :return: 学生对象
        """
        try:
            student = await Student.get(sid=student_id)
            if not student:
                return error(404, "学生未找到", {"student_id": student_id})
            return success(jsonable_encoder(student))
        except Exception as e:
            return error(400, f"获取学生信息失败: {str(e)}")
    
    
    async def update_student(student_id: int, student: StudentCreate) -> Dict[str, Any]:
        """
        更新学生信息业务逻辑
        :param student_id: 学生ID
        :param student: 学生更新信息
        :return: 更新后的学生对象
        """
        try:
            db_student = await Student.get(sid=student_id)
            if not db_student:
                return error(404, "学生未找到", {"student_id": student_id})
            updated_student = await db_student.update_from_dict(student.dict()).save()
            return success(jsonable_encoder(updated_student))
        except Exception as e:
            return error(400, f"更新学生信息失败: {str(e)}")
    
    
    async def delete_student(student_id: int) -> Dict[str, str]:
        """
        删除学生业务逻辑
        :param student_id: 学生ID
        :return: 删除结果
        """
        try:
            db_student = await Student.get(sid=student_id)
            if not db_student:
                return error(404, "学生未找到", {"student_id": student_id})
            await db_student.delete()
            return success(message="学生删除成功")
        except Exception as e:
            return error(400, f"删除学生失败: {str(e)}")
    
    
    async def create_course(course: CourseCreate) -> Dict[str, Any]:
        """
        创建课程业务逻辑
        :param course: 课程创建信息
        :return: 创建的课程对象
        """
        try:
            course_obj = await Course.create(**course.dict())
            return success(jsonable_encoder(course_obj))
        except Exception as e:
            return error(400, f"创建课程失败: {str(e)}")
    
    
    async def get_course(course_id: int) -> Dict[str, Any]:
        """
        获取课程信息业务逻辑
        :param course_id: 课程ID
        :return: 课程对象
        """
        try:
            course = await Course.get(cid=course_id)
            if not course:
                return error(404, "课程未找到", {"course_id": course_id})
            return success(jsonable_encoder(course))
        except Exception as e:
            return error(400, f"获取课程信息失败: {str(e)}")
    
    
    async def create_score(score: SCCreate) -> Dict[str, Any]:
        """
        创建成绩业务逻辑
        :param score: 成绩创建信息
        :return: 创建的成绩对象
        """
        try:
            score_obj = await SC.create(**score.dict())
            return success(jsonable_encoder(score_obj))
        except Exception as e:
            return error(400, f"创建成绩失败: {str(e)}")
    
    
    async def update_course(course_id: int, course: CourseCreate) -> Dict[str, Any]:
        """
        更新课程信息业务逻辑
        :param course_id: 课程ID
        :param course: 课程更新信息
        :return: 更新后的课程对象
        """
        try:
            db_course = await Course.get(cid=course_id)
            if not db_course:
                return error(404, "课程未找到", {"course_id": course_id})
            updated_course = await db_course.update_from_dict(course.dict()).save()
            return success(jsonable_encoder(updated_course))
        except Exception as e:
            return error(400, f"更新课程信息失败: {str(e)}")
    
    
    async def delete_course(course_id: int) -> Dict[str, str]:
        """
        删除课程业务逻辑
        :param course_id: 课程ID
        :return: 删除结果
        """
        try:
            db_course = await Course.get(cid=course_id)
            if not db_course:
                return error(404, "课程未找到", {"course_id": course_id})
            await db_course.delete()
            return success(message="课程删除成功")
        except Exception as e:
            return error(400, f"删除课程失败: {str(e)}")
    
    
    async def get_courses_list() -> Dict[str, Any]:
        """
        获取所有课程列表业务逻辑
        :return: 所有课程列表
        """
        try:
            courses = await Course.all()
            if not courses:
                return error(404, "课程信息获取失败")
            return success(jsonable_encoder(courses))
        except Exception as e:
            return error(400, f"获取课程列表失败: {str(e)}")
    
    
    async def get_scores(student_id: int) -> Dict[str, Any]:
        """
        获取学生成绩业务逻辑
        :param student_id: 学生ID
        :return: 该学生的所有成绩列表
        """
        try:
            scores = await SC.filter(sid=student_id).all()
            if not scores:
                return error(404, "未找到该学生的成绩记录", {"student_id": student_id})
            return success(jsonable_encoder(scores))
        except Exception as e:
            return error(400, f"获取成绩信息失败: {str(e)}")
    
    
    async def get_student_with_scores(student_id: int) -> Dict[str, Any]:
        """
        获取学生成绩详细信息,连表查询
        :param student_id: 学生ID
        :return: 学生及其成绩列表
        """
        try:
            # 获取学生基本信息
            student = await Student.get(sid=student_id)
            if not student:
                return error(404, "学生未找到", {"student_id": student_id})
    
            # 获取学生所有成绩记录
            scores = await SC.filter(sid=student_id).all()
            if not scores:
                return error(404, "该学生暂无成绩记录", {"student_id": student_id})
    
            # 获取每门课程的信息
            course_scores = []
            for score in scores:
                course = await Course.get(cid=score.cid)
                if course:
                    course_scores.append({
                        'course_id': course.cid,
                        'course_name': course.cname,
                        'score': score.score,
                    })
    
            result = {
                'student_id': student.sid,
                'student_name': student.sname,
                'student_age': student.sage,
                'student_sex': student.ssex,
                'courses': course_scores,
                'average_score': sum(course['score'] for course in course_scores) / len(
                    course_scores) if course_scores else 0
            }
            return success(jsonable_encoder(result))
        except Exception as e:
            return error(400, f"获取学生成绩信息失败: {str(e)}")
    
    
    async def get_teacher_with_courses(teacher_id: int) -> Dict[str, Any]:
        """
        获取教师所教课程信息,连表查询
        :param teacher_id: 教师ID
        :return: 教师及其所教课程列表
        """
        try:
            # 获取教师基本信息
            teacher = await Teacher.get(tid=teacher_id)
            if not teacher:
                return error(404, "教师未找到", {"teacher_id": teacher_id})
    
            # 获取教师所教的所有课程
            courses = await Course.filter(tid=teacher_id).all()
            if not courses:
                return error(404, "该教师暂无授课课程", {"teacher_id": teacher_id})
    
            # 获取每门课程的选课学生数量
            course_details = []
            for course in courses:
                student_count = await SC.filter(cid=course.cid).count()
                course_details.append({
                    'course_id': course.cid,
                    'course_name': course.cname,
                    'student_count': student_count
                })
    
            result = {
                'teacher_id': teacher.tid,
                'teacher_name': teacher.tname,
                'courses': course_details,
                'total_courses': len(course_details),
                'total_students': sum(course['student_count'] for course in course_details)
            }
            return success(jsonable_encoder(result))
        except Exception as e:
            return error(400, f"获取教师课程信息失败: {str(e)}")
    
    
    async def get_course_students(course_id: int) -> Dict[str, Any]:
        """
        获取课程的学生列表信息
        :param course_id: 课程ID
        :return: 课程及其学生列表
        """
        try:
            # 获取课程基本信息
            course = await Course.get(cid=course_id)
            if not course:
                return error(404, "课程未找到", {"course_id": course_id})
    
            # 获取选修该课程的所有学生成绩记录
            scores = await SC.filter(cid=course_id).all()
            if not scores:
                return error(404, "暂无学生选修该课程", {"course_id": course_id})
    
            # 获取每个学生的详细信息
            student_details = []
            for score in scores:
                student = await Student.get(sid=score.sid)
                if student:
                    student_details.append({
                        'student_id': student.sid,
                        'student_name': student.sname,
                        'student_age': student.sage,
                        'student_sex': student.ssex,
                        'score': score.score
                    })
    
            # 计算课程统计信息
            total_students = len(student_details)
            average_score = sum(
                student['score'] for student in student_details) / total_students if total_students > 0 else 0
            max_score = max(student['score'] for student in student_details) if student_details else 0
            min_score = min(student['score'] for student in student_details) if student_details else 0
    
            result = {
                'course_id': course.cid,
                'course_name': course.cname,
                'teacher_id': course.tid,
                'students': student_details,
                'statistics': {
                    'total_students': total_students,
                    'average_score': average_score,
                    'max_score': max_score,
                    'min_score': min_score
                }
            }
            return success(jsonable_encoder(result))
        except Exception as e:
            return error(400, f"获取课程学生列表失败: {str(e)}")
    

     Step5:创建后端接口(路由)

    根据引用业务实现具体的python后端接口,可类比于springboot中的controller,但是在fastapi中它也叫路由。

    from fastapi import APIRouter
    
    # 在导入部分添加课程相关服务
    from app.application.services import (
        create_teacher, get_teacher, update_teacher, delete_teacher,
        create_student, get_student, update_student, delete_student,
        create_score, get_scores,
        get_student_with_scores,
        get_teacher_with_courses, create_course, get_course, delete_course,
        get_course_students, update_course, get_courses_list
    )
    from app.core.result import Result
    from app.schemas.schemas import TeacherCreate, StudentCreate, SCCreate, CourseCreate
    
    router = APIRouter(prefix="/api/v1", tags=["API接口"])
    
    
    @router.post("/teachers/", response_model=Result, summary="创建教师", description="创建一个新的教师记录")
    async def create_teacher_endpoint(teacher: TeacherCreate):
        return await create_teacher(teacher)
    
    
    @router.get("/teachers/{teacher_id}", response_model=Result, summary="获取教师信息",
                description="根据教师ID获取教师信息")
    async def get_teacher_endpoint(teacher_id: int):
        return await get_teacher(teacher_id)
    
    
    @router.post("/students/add", response_model=Result, summary="创建学生", description="创建一个新的学生记录")
    async def create_student_endpoint(student: StudentCreate):
        return await create_student(student)
    
    
    @router.get("/students/{student_id}", response_model=Result, summary="获取学生信息",
                description="根据学生ID获取学生信息")
    async def get_student_endpoint(student_id: int):
        return await get_student(student_id)
    
    
    @router.post("/scores/", response_model=Result, summary="创建成绩", description="创建一个新的成绩记录")
    async def create_score_endpoint(score: SCCreate):
        return await create_score(score)
    
    
    @router.get("/scores/{student_id}", response_model=Result, summary="获取学生成绩",
                description="根据学生ID获取该学生的所有成绩")
    async def get_scores_endpoint(student_id: int):
        return await get_scores(student_id)
    
    
    @router.put("/teachers/{teacher_id}", response_model=Result, summary="更新教师信息")
    async def update_teacher_endpoint(teacher_id: int, teacher: TeacherCreate):
        return await update_teacher(teacher_id, teacher)
    
    
    @router.delete("/teachers/{teacher_id}", response_model=Result, summary="删除教师")
    async def delete_teacher_endpoint(teacher_id: int):
        return await delete_teacher(teacher_id)
    
    
    @router.put("/students/update/{student_id}", response_model=Result, summary="更新学生信息")
    async def update_student_endpoint(student_id: int, student: StudentCreate):
        return await update_student(student_id, student)
    
    
    @router.delete("/students/{student_id}", response_model=Result, summary="删除学生")
    async def delete_student_endpoint(student_id: int):
        return await delete_student(student_id)
    
    
    @router.get("/students/{student_id}/scores", response_model=Result, summary="获取学生及其所有成绩",
                description="根据学生ID获取学生信息和所有成绩记录")
    async def get_student_with_scores_endpoint(student_id: int):
        return await get_student_with_scores(student_id)
    
    
    @router.get("/teachers/{teacher_id}/courses", response_model=Result, summary="获取教师及其授课课程",
                description="根据教师ID获取教师信息和所授课程")
    async def get_teacher_with_courses_endpoint(teacher_id: int):
        return await get_teacher_with_courses(teacher_id)
    
    
    @router.post("/courses/", response_model=Result, summary="创建课程", description="创建一个新的课程记录")
    async def create_course_endpoint(course: CourseCreate):
        return await create_course(course)
    
    
    @router.get("/courses/{course_id}", response_model=Result, summary="获取课程信息")
    async def get_course_endpoint(course_id: int):
        return await get_course(course_id)
    
    
    @router.put("/courses/update/{course_id}", response_model=Result, summary="更新课程信息")
    async def update_course_endpoint(course_id: int, course: CourseCreate):
        return await update_course(course_id, course)
    
    
    @router.delete("/courses/del/{course_id}", response_model=Result, summary="删除课程")
    async def delete_course_endpoint(course_id: int):
        return await delete_course(course_id)
    
    
    @router.delete("/courses/list", response_model=Result, summary="获取所有课程")
    async def course_list():
        return await get_courses_list()
    
    
    @router.get("/courses/{course_id}/students", response_model=Result, summary="获取课程及其学生列表",
                description="根据课程ID获取课程信息和选修该课程的学生列表")
    async def get_course_students_endpoint(course_id: int):
        return await get_course_students(course_id)
    
    
    

     Step6:项目运行及测试

    在main.py中配置之前咱们配置过的数据库初始信息,全局异常等信息,有需求的可以加上swagger的一些信息,但是由于cdn原因,fastapi中swagger访问会出现点问题,需要下载相应的静态文件到本地,这里不做示例,大家可以根据自己情况自行配置:

    from contextlib import asynccontextmanager
    
    from fastapi import FastAPI
    
    from app.core.logger import app_logger, tortoise_logger, db_logger
    from app.infrastructure.database import init_database, close_database, register_database
    from app.routers import api
    
    
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        # 启动时执行
        app_logger.info("Application startup")
        tortoise_logger.info("Tortoise ORM startup")
        await init_database()
        yield
        # 关闭时执行
        app_logger.info("Application shutdown")
        tortoise_logger.info("Tortoise ORM shutdown")
        await close_database()
    
    
    app = FastAPI(
        title="Student Management System",
        version="1.0.0",
        lifespan=lifespan
    )
    
    # 注册路由
    app.include_router(api.router)
    
    # 注册数据库
    register_database(app)
    
    # 配置 Tortoise ORM 日志
    import logging
    from tortoise.log import logger
    
    # 设置 Tortoise ORM 的日志级别
    logger.setLevel(logging.DEBUG)  # 设置为DEBUG级别以记录所有SQL查询
    
    # 添加自定义处理器到 Tortoise ORM 的日志记录器
    for handler in db_logger.handlers:
        logger.addHandler(handler)
    
    if __name__ == "__main__":
        import uvicorn
    
        uvicorn.run(app, host="0.0.0.0", port=8009)
    

    然后通过测试工具对fastapi的这些接口测试:

    完成后我们可以通过控制台日志或者日志文件查看到orm框架在执行业务时的sql信息了,这样方便我们出现问题可以方便检查:

    INFO:     Started server process [2712]
    INFO:     Waiting for application startup.
    2025-04-12 20:21:03,974 - tortoise - DEBUG - [__init__.py:505] - Tortoise-ORM startup
        connections: {'default': 'asyncpg://postgres:12***@localhost:5432/python_test'}
        apps: {'models': {'models': ['app.models.models'], 'default_connection': 'default'}}
    2025-04-12 20:21:04,077 - tortoise - INFO - [__init__.py:163] - Tortoise-ORM started, {'default': <tortoise.backends.asyncpg.client.AsyncpgDBClient object at 0x000001F73A3DCE90>}, {'models': {'Course': <class 'app.models.models.Course'>, 'SC': <class 'app.models.models.SC'>, 'Student': <class 'app.models.models.Student'>, 'Teacher': <class 'app.models.models.Teacher'>}}
    2025-04-12 20:21:04,077 - app - INFO - [main.py:13] - Application startup
    2025-04-12 20:21:04,078 - tortoise - INFO - [main.py:14] - Tortoise ORM startup
    2025-04-12 20:21:04,078 - tortoise - DEBUG - [__init__.py:505] - Tortoise-ORM startup
        connections: {'default': {'engine': 'tortoise.backends.asyncpg', 'credentials': {'port': 5432, 'database': 'python_test', 'host': 'localhost', 'user': 'postgres', 'password': '12***'}}}
        apps: {'models': {'models': ['app.models.models'], 'default_connection': 'default'}}
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)
    2025-04-12 20:21:11,622 - tortoise.db_client - DEBUG - [client.py:62] - Created connection pool <asyncpg.pool.Pool object at 0x000001F73A517100> with params: {'host': 'localhost', 'port': 5432, 'user': 'postgres', 'database': 'python_test', 'min_size': 1, 'max_size': 5, 'connection_class': <class 'asyncpg.connection.Connection'>, 'loop': None, 'server_settings': {}}
    2025-04-12 20:21:11,623 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "tid","tname" FROM "teacher" WHERE "tid"=$1 LIMIT $2: [5, 2]
    INFO:     127.0.0.1:64560 - "GET /api/v1/teachers/5 HTTP/1.1" 200 OK
    2025-04-12 20:21:12,052 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "sage","sname","ssex","sid" FROM "student" WHERE "sid"=$1 LIMIT $2: [3, 2]
    INFO:     127.0.0.1:64560 - "GET /api/v1/students/3 HTTP/1.1" 200 OK
    2025-04-12 20:21:12,475 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "cid","id","score","sid" FROM "sc" WHERE "sid"=$1: [3]
    INFO:     127.0.0.1:64560 - "GET /api/v1/scores/3 HTTP/1.1" 200 OK
    2025-04-12 20:21:12,873 - tortoise.db_client - DEBUG - [client.py:110] - INSERT INTO "student" ("sname","sage","ssex") VALUES ($1,$2,$3) RETURNING "sid": ['赵东来', 22, '男']
    INFO:     127.0.0.1:64560 - "POST /api/v1/students/add HTTP/1.1" 200 OK
    2025-04-12 20:21:13,288 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "sage","sname","ssex","sid" FROM "student" WHERE "sid"=$1 LIMIT $2: [3, 2]
    2025-04-12 20:21:13,289 - tortoise.db_client - DEBUG - [client.py:132] - UPDATE "student" SET "sname"=$1,"sage"=$2,"ssex"=$3 WHERE "sid"=$4: ['黎治跃', 25, '男', 3]
    INFO:     127.0.0.1:64560 - "PUT /api/v1/students/update/3 HTTP/1.1" 200 OK
    2025-04-12 20:21:13,711 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "cname","cid","tid" FROM "course" WHERE "cid"=$1 LIMIT $2: [2, 2]
    2025-04-12 20:21:13,713 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "cid","id","score","sid" FROM "sc" WHERE "cid"=$1: [2]
    2025-04-12 20:21:13,714 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "sage","sname","ssex","sid" FROM "student" WHERE "sid"=$1 LIMIT $2: [1, 2]
    2025-04-12 20:21:13,715 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "sage","sname","ssex","sid" FROM "student" WHERE "sid"=$1 LIMIT $2: [5, 2]
    2025-04-12 20:21:13,716 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "sage","sname","ssex","sid" FROM "student" WHERE "sid"=$1 LIMIT $2: [1, 2]
    INFO:     127.0.0.1:64560 - "GET /api/v1/courses/2/students HTTP/1.1" 200 OK
    2025-04-12 20:21:14,154 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "tid","tname" FROM "teacher" WHERE "tid"=$1 LIMIT $2: [4, 2]
    2025-04-12 20:21:14,155 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "cname","cid","tid" FROM "course" WHERE "tid"=$1: [4]
    2025-04-12 20:21:14,156 - tortoise.db_client - DEBUG - [client.py:132] - SELECT COUNT(*) FROM "sc" WHERE "cid"=$1: [6]
    2025-04-12 20:21:14,158 - tortoise.db_client - DEBUG - [client.py:132] - SELECT COUNT(*) FROM "sc" WHERE "cid"=$1: [10]
    2025-04-12 20:21:14,159 - tortoise.db_client - DEBUG - [client.py:132] - SELECT COUNT(*) FROM "sc" WHERE "cid"=$1: [11]
    INFO:     127.0.0.1:64560 - "GET /api/v1/teachers/4/courses HTTP/1.1" 200 OK
    2025-04-12 20:21:14,583 - tortoise.db_client - DEBUG - [client.py:110] - INSERT INTO "course" ("cname","tid") VALUES ($1,$2) RETURNING "cid": ['毛泽东选集', 3]
    INFO:     127.0.0.1:64560 - "POST /api/v1/courses/ HTTP/1.1" 200 OK
    2025-04-12 20:21:14,976 - tortoise.db_client - DEBUG - [client.py:132] - SELECT "cname","cid","tid" FROM "course" WHERE "cid"=$1 LIMIT $2: [31, 2]
    2025-04-12 20:21:14,977 - tortoise.db_client - DEBUG - [client.py:132] - DELETE FROM "course" WHERE "cid"=$1: [31]
    INFO:     127.0.0.1:64560 - "DELETE /api/v1/courses/del/31 HTTP/1.1" 200 OK

    并且完成了响应数据的规范化:

    四、总结 

     可以看到,其实FastAPI 与 Tortoise-ORM 的组合在 Python Web 开发领域还是比较方便的,特别是对于纯后端开发者来说,接受起来并不太困难。

    虽然本文并未对其在并发开发中的效能进行具体介绍,但是FastAPI 具备高效处理高并发请求的能力,Tortoise-ORM 则在异步操作方面性能卓越,二者协同构建了完整的异步链路,相信可以有效提升了系统的吞吐量,降低了响应延迟。当然了肯定不能和Go语言相对比,具体测试大家可以看一下知名性能评测博主 Anton Putra Python (FastAPI) vs Go (Golang) Performance Benchmark。

    但是我认为也算是Python Web开发的一次提升吧。

    此外呢,该组合在开发效率、代码维护及性能优化等方面表现突出,尤其适用于微服务、物联网(IoT)、实时系统等高要求场景,为 Python Web 开发者提供了全面优化的开发体验,无疑是当前构建高性能应用的理想选择。感兴趣的小伙伴可以试试~

    作者:深情不及里子

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python异步开发实践:FastAPI与Tortoise-ORM的高效协同工作指南

    发表回复